[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
@@ -13,11 +13,11 @@ package org.jetbrains.kotlin.cli.js.klib
import com.intellij.openapi.project.Project
import org.jetbrains.kotlin.analyzer.AnalysisResult
import org.jetbrains.kotlin.backend.common.extensions.IrPluginContext
import org.jetbrains.kotlin.backend.common.linkage.issues.checkNoUnboundSymbols
import org.jetbrains.kotlin.backend.common.lower.ExpectDeclarationRemover
import org.jetbrains.kotlin.backend.common.overrides.FakeOverrideChecker
import org.jetbrains.kotlin.backend.common.serialization.DescriptorByIdSignatureFinderImpl
import org.jetbrains.kotlin.backend.common.serialization.ICData
import org.jetbrains.kotlin.backend.common.serialization.linkerissues.checkNoUnboundSymbols
import org.jetbrains.kotlin.backend.common.serialization.mangle.ManglerChecker
import org.jetbrains.kotlin.backend.common.serialization.mangle.descriptor.Ir2DescriptorManglerAdapter
import org.jetbrains.kotlin.backend.common.serialization.signature.IdSignatureDescriptor
@@ -15,6 +15,7 @@ import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.expressions.IrCall
import org.jetbrains.kotlin.ir.expressions.IrExpression
import org.jetbrains.kotlin.ir.expressions.IrStatementOrigin
import org.jetbrains.kotlin.ir.linkage.partial.PartialLinkageSupportForLowerings
import org.jetbrains.kotlin.ir.symbols.IrClassSymbol
import org.jetbrains.kotlin.ir.symbols.IrFileSymbol
import org.jetbrains.kotlin.ir.symbols.IrSimpleFunctionSymbol
@@ -82,6 +83,9 @@ interface CommonBackendContext : BackendContext, LoggingContext, ErrorReportingC
*/
val inlineClassesUtils: InlineClassesUtils
get() = DefaultInlineClassesUtils
val partialLinkageSupport: PartialLinkageSupportForLowerings
get() = PartialLinkageSupportForLowerings.DISABLED
}
/**
@@ -12,7 +12,6 @@ import org.jetbrains.kotlin.builtins.StandardNames
import org.jetbrains.kotlin.builtins.StandardNames.KOTLIN_REFLECT_FQ_NAME
import org.jetbrains.kotlin.ir.IrBuiltIns
import org.jetbrains.kotlin.ir.declarations.IrFunction
import org.jetbrains.kotlin.ir.declarations.IrModuleFragment
import org.jetbrains.kotlin.ir.declarations.IrPackageFragment
import org.jetbrains.kotlin.ir.symbols.IrClassSymbol
import org.jetbrains.kotlin.ir.symbols.IrClassifierSymbol
@@ -537,13 +537,16 @@ class LocalDeclarationsLowering(
// NOTE: if running before InitializersLowering, we can instead look for constructors that have
// IrInstanceInitializerCall. However, Native runs these two lowerings in opposite order.
val constructorsCallingSuper = constructors
val constructorsByDelegationKinds: Map<ConstructorDelegationKind, List<LocalClassConstructorContext>> = constructors
.asSequence()
.map { localClassConstructors[it]!! }
.filter { it.declaration.callsSuper(context.irBuiltIns) }
.toList()
.groupBy { it.declaration.delegationKind(context.irBuiltIns) }
assert(constructorsCallingSuper.any()) { "Expected at least one constructor calling super; class: $irClass" }
val constructorsCallingSuper = constructorsByDelegationKinds[ConstructorDelegationKind.CALLS_SUPER].orEmpty()
assert(constructorsCallingSuper.isNotEmpty() || constructorsByDelegationKinds[ConstructorDelegationKind.PARTIAL_LINKAGE_ERROR] != null) {
"Expected at least one constructor calling super; class: $irClass"
}
val usedCaptureFields = createFieldsForCapturedValues(localClassContext)
irClass.declarations += usedCaptureFields
@@ -1049,7 +1052,7 @@ class LocalDeclarationsLowering(
// other restrictions on IR (e.g. after the initializers are moved you can no longer create fields
// with initializers) which makes that hard to implement.
val constructorContext = declaration.constructors.mapNotNull { localClassConstructors[it] }
.singleOrNull { it.declaration.callsSuper(context.irBuiltIns) }
.singleOrNull { it.declaration.delegationKind(context.irBuiltIns) == ConstructorDelegationKind.CALLS_SUPER }
localClasses[declaration] = LocalClassContext(declaration, data.inInlineFunctionScope, constructorContext)
}
@@ -16,6 +16,7 @@ import org.jetbrains.kotlin.ir.builders.*
import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.expressions.*
import org.jetbrains.kotlin.ir.expressions.impl.*
import org.jetbrains.kotlin.ir.linkage.partial.isPartialLinkageRuntimeError
import org.jetbrains.kotlin.ir.symbols.IrSymbol
import org.jetbrains.kotlin.ir.symbols.IrValueParameterSymbol
import org.jetbrains.kotlin.ir.symbols.IrValueSymbol
@@ -168,14 +169,26 @@ open class IrBuildingTransformer(private val context: BackendContext) : IrElemen
}
}
fun IrConstructor.callsSuper(irBuiltIns: IrBuiltIns): Boolean {
enum class ConstructorDelegationKind {
/** Calls another constructor of the same class. */
CALLS_THIS,
/** Calls the constructor of the super class. */
CALLS_SUPER,
/** The actual delegation is not known. The constructor call was replaced by a partial linkage error. */
PARTIAL_LINKAGE_ERROR
}
fun IrConstructor.delegationKind(irBuiltIns: IrBuiltIns): ConstructorDelegationKind {
val constructedClass = parent as IrClass
val superClass = constructedClass.superTypes
.mapNotNull { it as? IrSimpleType }
.firstOrNull { (it.classifier.owner as IrClass).run { kind == ClassKind.CLASS || kind == ClassKind.ANNOTATION_CLASS || kind == ClassKind.ENUM_CLASS } }
?: irBuiltIns.anyType
var callsSuper = false
var numberOfCalls = 0
var numberOfDelegatingCalls = 0
var hasPartialLinkageError = false
acceptChildrenVoid(object : IrElementVisitorVoid {
override fun visitElement(element: IrElement) {
element.acceptChildrenVoid(this)
@@ -186,7 +199,7 @@ fun IrConstructor.callsSuper(irBuiltIns: IrBuiltIns): Boolean {
}
override fun visitDelegatingConstructorCall(expression: IrDelegatingConstructorCall) {
assert(++numberOfCalls == 1) { "More than one delegating constructor call: ${symbol.owner}" }
numberOfDelegatingCalls++
val delegatingClass = expression.symbol.owner.parent as IrClass
// TODO: figure out why Lazy IR multiplies Declarations for descriptors and fix it
// It happens because of IrBuiltIns whose IrDeclarations are different for runtime and test
@@ -198,11 +211,31 @@ fun IrConstructor.callsSuper(irBuiltIns: IrBuiltIns): Boolean {
" call to super class constructor. But was: $delegatingClass with '${delegatingClass.name}' name"
)
}
override fun visitExpression(expression: IrExpression) {
hasPartialLinkageError = hasPartialLinkageError || expression.isPartialLinkageRuntimeError()
super.visitExpression(expression)
}
})
assert(numberOfCalls == 1) { "Expected exactly one delegating constructor call but none encountered: ${symbol.owner}" }
return callsSuper
val delegationKind: ConstructorDelegationKind? = when (numberOfDelegatingCalls) {
0 -> if (hasPartialLinkageError) ConstructorDelegationKind.PARTIAL_LINKAGE_ERROR else null
1 -> if (callsSuper) ConstructorDelegationKind.CALLS_SUPER else ConstructorDelegationKind.CALLS_THIS
else -> null
}
if (delegationKind != null)
return delegationKind
else
throw AssertionError("Expected exactly one delegating constructor call but $numberOfDelegatingCalls encountered: ${symbol.owner}")
}
@Deprecated(
"Replaced by delegationKind() that is aware of the possible partial linkage side effects",
ReplaceWith("delegationKind(irBuiltIns)")
)
fun IrConstructor.callsSuper(irBuiltIns: IrBuiltIns): Boolean = delegationKind(irBuiltIns) == ConstructorDelegationKind.CALLS_SUPER
fun ParameterDescriptor.copyAsValueParameter(newOwner: CallableDescriptor, index: Int, name: Name = this.name) = when (this) {
is ValueParameterDescriptor -> this.copy(newOwner, name, index)
is ReceiverParameterDescriptor -> ValueParameterDescriptorImpl(
@@ -11,13 +11,13 @@ import org.jetbrains.kotlin.backend.common.lower.createIrBuilder
import org.jetbrains.kotlin.backend.common.runOnFilePostfix
import org.jetbrains.kotlin.ir.builders.irGet
import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.expressions.IrBody
import org.jetbrains.kotlin.ir.expressions.IrCall
import org.jetbrains.kotlin.ir.expressions.IrExpression
import org.jetbrains.kotlin.ir.expressions.*
import org.jetbrains.kotlin.ir.linkage.partial.PartialLinkageCase.SuspendableFunctionCallWithoutCoroutineContext
import org.jetbrains.kotlin.ir.util.irCall
import org.jetbrains.kotlin.ir.util.isSuspend
import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid
import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid
import org.jetbrains.kotlin.ir.linkage.partial.PartialLinkageUtils.File as PLFile
/**
* Add continuation to suspend function calls.
@@ -25,21 +25,23 @@ import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid
* Additionally materialize continuation for `getContinuation` intrinsic calls.
*/
abstract class AbstractAddContinuationToFunctionCallsLowering : BodyLoweringPass {
abstract val context: CommonBackendContext
protected abstract val context: CommonBackendContext
protected abstract fun IrSimpleFunction.isContinuationItself(): Boolean
override fun lower(irFile: IrFile) {
runOnFilePostfix(irFile, withLocalDeclarations = true)
}
abstract fun IrSimpleFunction.getContinuationParameter() : IrValueParameter
override fun lower(irBody: IrBody, container: IrDeclaration) {
val continuation: IrValueParameter by lazy {
val continuation: IrValueParameter? by lazy {
(container as IrSimpleFunction).getContinuationParameter()
}
val builder by lazy { context.createIrBuilder(container.symbol) }
fun getContinuation() = builder.irGet(continuation)
fun getContinuation(): IrGetValue? = continuation?.let(builder::irGet)
val plFile: PLFile by lazy { PLFile.determineFileFor(container) }
irBody.transformChildrenVoid(object : IrElementTransformerVoid() {
override fun visitBody(body: IrBody): IrBody {
@@ -52,7 +54,7 @@ abstract class AbstractAddContinuationToFunctionCallsLowering : BodyLoweringPass
if (!expression.isSuspend) {
if (expression.symbol == context.ir.symbols.getContinuation)
return getContinuation()
return getContinuation() ?: expression.throwLinkageError(plFile)
return expression
}
@@ -65,11 +67,40 @@ abstract class AbstractAddContinuationToFunctionCallsLowering : BodyLoweringPass
newReturnType = newFun.returnType,
newSuperQualifierSymbol = expression.superQualifierSymbol
).also {
it.putValueArgument(it.valueArgumentsCount - 1, getContinuation())
it.putValueArgument(it.valueArgumentsCount - 1, getContinuation() ?: return expression.throwLinkageError(plFile))
}
}
})
}
// IMPORTANT: May return null only if partial linkage is turned on.
private fun IrSimpleFunction.getContinuationParameter(): IrValueParameter? {
if (isContinuationItself())
return dispatchReceiverParameter!!
else {
val isLoweredSuspendFunction = origin == IrDeclarationOrigin.LOWERED_SUSPEND_FUNCTION
if (!isLoweredSuspendFunction) {
return if (context.partialLinkageSupport.isEnabled)
null
else
throw IllegalArgumentException("Continuation parameter only exists in lowered suspend functions, but function origin is $origin")
}
val continuation = valueParameters.lastOrNull()
require(continuation != null && continuation.origin == IrDeclarationOrigin.CONTINUATION) {
"Continuation parameter is expected to be the last one"
}
return continuation
}
}
private fun IrCall.throwLinkageError(file: PLFile): IrCall =
context.partialLinkageSupport.throwLinkageError(
SuspendableFunctionCallWithoutCoroutineContext(this),
element = this,
file,
suppressWarningInCompilerOutput = false
)
}
@@ -8,6 +8,7 @@ package org.jetbrains.kotlin.ir.backend.js
import org.jetbrains.kotlin.backend.common.compilationException
import org.jetbrains.kotlin.backend.common.ir.Ir
import org.jetbrains.kotlin.backend.common.ir.Symbols
import org.jetbrains.kotlin.backend.common.linkage.partial.createPartialLinkageSupportForLowerings
import org.jetbrains.kotlin.builtins.PrimitiveType
import org.jetbrains.kotlin.builtins.isFunctionType
import org.jetbrains.kotlin.config.CompilerConfiguration
@@ -35,10 +36,7 @@ import org.jetbrains.kotlin.ir.symbols.*
import org.jetbrains.kotlin.ir.symbols.impl.DescriptorlessExternalPackageFragmentSymbol
import org.jetbrains.kotlin.ir.types.*
import org.jetbrains.kotlin.ir.types.impl.IrDynamicTypeImpl
import org.jetbrains.kotlin.ir.util.SymbolTable
import org.jetbrains.kotlin.ir.util.getAnnotation
import org.jetbrains.kotlin.ir.util.kotlinPackageFqn
import org.jetbrains.kotlin.ir.util.render
import org.jetbrains.kotlin.ir.util.*
import org.jetbrains.kotlin.js.backend.ast.JsExpressionStatement
import org.jetbrains.kotlin.js.backend.ast.JsFunction
import org.jetbrains.kotlin.js.config.ErrorTolerancePolicy
@@ -409,4 +407,10 @@ class JsIrBackendContext(
outlinedJsCodeFunctions[symbol] = parsedJsFunction
return parsedJsFunction
}
override val partialLinkageSupport = createPartialLinkageSupportForLowerings(
isEnabled = configuration[JSConfigurationKeys.PARTIAL_LINKAGE] ?: false,
irBuiltIns,
configuration.irMessageLogger
)
}
@@ -5,9 +5,9 @@
package org.jetbrains.kotlin.ir.backend.js
import org.jetbrains.kotlin.backend.common.linkage.issues.checkNoUnboundSymbols
import org.jetbrains.kotlin.backend.common.phaser.PhaseConfig
import org.jetbrains.kotlin.backend.common.phaser.invokeToplevel
import org.jetbrains.kotlin.backend.common.serialization.linkerissues.checkNoUnboundSymbols
import org.jetbrains.kotlin.config.CompilerConfiguration
import org.jetbrains.kotlin.ir.IrBuiltIns
import org.jetbrains.kotlin.ir.backend.js.codegen.JsGenerationGranularity
@@ -5,10 +5,10 @@
package org.jetbrains.kotlin.ir.backend.js.ic
import org.jetbrains.kotlin.backend.common.linkage.issues.checkNoUnboundSymbols
import org.jetbrains.kotlin.backend.common.serialization.DeserializationStrategy
import org.jetbrains.kotlin.backend.common.serialization.checkIsFunctionInterface
import org.jetbrains.kotlin.backend.common.serialization.encodings.BinarySymbolData
import org.jetbrains.kotlin.backend.common.serialization.linkerissues.checkNoUnboundSymbols
import org.jetbrains.kotlin.backend.common.serialization.signature.IdSignatureDescriptor
import org.jetbrains.kotlin.config.CompilerConfiguration
import org.jetbrains.kotlin.config.languageVersionSettings
@@ -6,22 +6,20 @@
package org.jetbrains.kotlin.ir.backend.js.lower.coroutines
import org.jetbrains.kotlin.backend.common.lower.coroutines.AbstractAddContinuationToFunctionCallsLowering
import org.jetbrains.kotlin.backend.common.lower.coroutines.AddContinuationToLocalSuspendFunctionsLowering
import org.jetbrains.kotlin.backend.common.lower.coroutines.AddContinuationToNonLocalSuspendFunctionsLowering
import org.jetbrains.kotlin.ir.backend.js.JsCommonBackendContext
import org.jetbrains.kotlin.ir.declarations.IrSimpleFunction
import org.jetbrains.kotlin.ir.declarations.IrValueParameter
import org.jetbrains.kotlin.js.config.JSConfigurationKeys
/**
* Requires [AddContinuationToLocalSuspendFunctionsLowering] and
* [AddContinuationToNonLocalSuspendFunctionsLowering] to transform function declarations first.
*/
class AddContinuationToFunctionCallsLowering(override val context: JsCommonBackendContext) : AbstractAddContinuationToFunctionCallsLowering() {
override fun IrSimpleFunction.getContinuationParameter(): IrValueParameter =
if (overriddenSymbols.any {
it.owner.name.asString() == "doResume" && it.owner.parent == context.coroutineSymbols.coroutineImpl.owner
}
) {
dispatchReceiverParameter!!
} else {
valueParameters.last()
}
class AddContinuationToFunctionCallsLowering(
override val context: JsCommonBackendContext
) : AbstractAddContinuationToFunctionCallsLowering() {
override fun IrSimpleFunction.isContinuationItself(): Boolean = overriddenSymbols.any { overriddenSymbol ->
overriddenSymbol.owner.name.asString() == "doResume" && overriddenSymbol.owner.parent == context.coroutineSymbols.coroutineImpl.owner
}
}
@@ -10,11 +10,11 @@ import org.jetbrains.kotlin.backend.common.extensions.FirIncompatiblePluginAPI
import org.jetbrains.kotlin.backend.common.extensions.IrGenerationExtension
import org.jetbrains.kotlin.backend.common.extensions.IrPluginContext
import org.jetbrains.kotlin.backend.common.extensions.IrPluginContextImpl
import org.jetbrains.kotlin.backend.common.linkage.issues.checkNoUnboundSymbols
import org.jetbrains.kotlin.backend.common.phaser.CompilerPhase
import org.jetbrains.kotlin.backend.common.phaser.PhaseConfig
import org.jetbrains.kotlin.backend.common.phaser.invokeToplevel
import org.jetbrains.kotlin.backend.common.serialization.DescriptorByIdSignatureFinderImpl
import org.jetbrains.kotlin.backend.common.serialization.linkerissues.checkNoUnboundSymbols
import org.jetbrains.kotlin.backend.jvm.codegen.JvmIrIntrinsicExtension
import org.jetbrains.kotlin.backend.jvm.intrinsics.IrIntrinsicMethods
import org.jetbrains.kotlin.backend.jvm.ir.getIoFile
@@ -5,9 +5,9 @@
package org.jetbrains.kotlin.backend.wasm
import org.jetbrains.kotlin.backend.common.linkage.issues.checkNoUnboundSymbols
import org.jetbrains.kotlin.backend.common.phaser.PhaseConfig
import org.jetbrains.kotlin.backend.common.phaser.invokeToplevel
import org.jetbrains.kotlin.backend.common.serialization.linkerissues.checkNoUnboundSymbols
import org.jetbrains.kotlin.backend.wasm.ir2wasm.JsModuleAndQualifierReference
import org.jetbrains.kotlin.backend.wasm.ir2wasm.WasmCompiledModuleFragment
import org.jetbrains.kotlin.backend.wasm.ir2wasm.WasmModuleFragmentGenerator
@@ -25,9 +25,9 @@ import org.jetbrains.kotlin.ir.util.patchDeclarationParents
import org.jetbrains.kotlin.js.sourceMap.SourceFilePathResolver
import org.jetbrains.kotlin.js.sourceMap.SourceMap3Builder
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.wasm.ir.source.location.SourceLocation
import org.jetbrains.kotlin.wasm.ir.convertors.WasmIrToBinary
import org.jetbrains.kotlin.wasm.ir.convertors.WasmIrToText
import org.jetbrains.kotlin.wasm.ir.source.location.SourceLocation
import org.jetbrains.kotlin.wasm.ir.source.location.SourceLocationMapping
import java.io.ByteArrayOutputStream
import java.io.File
@@ -92,6 +92,8 @@ interface IrStatementOrigin {
object SYNTHETIC_NOT_AUTOBOXED_CHECK : IrStatementOriginImpl("SYNTHETIC_NOT_AUTOBOXED_CHECK")
object PARTIAL_LINKAGE_RUNTIME_ERROR : IrStatementOriginImpl("PARTIAL_LINKAGE_RUNTIME_ERROR")
data class COMPONENT_N private constructor(val index: Int) : IrStatementOriginImpl("COMPONENT_$index") {
companion object {
private val precreatedComponents = Array(32) { i -> COMPONENT_N(i + 1) }
@@ -0,0 +1,59 @@
/*
* 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.ir.linkage.partial
import org.jetbrains.kotlin.ir.symbols.IrClassSymbol
import org.jetbrains.kotlin.ir.symbols.IrClassifierSymbol
import kotlin.reflect.KClass
/**
* Describes the reason why a certain classifier is considered as unusable (partially linked).
* For more details see [ClassifierExplorer.exploreSymbol].
*/
@Suppress("KDocUnresolvedReference")
sealed interface ExploredClassifier {
/** Indicated unusable classifier. */
sealed interface Unusable : ExploredClassifier {
val symbol: IrClassifierSymbol
sealed interface CanBeRootCause : Unusable
/**
* There is no real owner classifier for the symbol, only synthetic stub created by [MissingDeclarationStubGenerator].
* Likely the classifier has been deleted in newer version of the library.
*/
data class MissingClassifier(override val symbol: IrClassifierSymbol) : CanBeRootCause
/**
* There is an issue with inheritance: interface inherits from a class, class inherits from a final class, etc.
* On practice, such class can't be instantiated and used anywhere.
*/
class InvalidInheritance(override val symbol: IrClassSymbol, val superClassSymbols: Collection<IrClassSymbol>) : CanBeRootCause {
init {
// Just a sanity check to avoid creating invalid [InvalidInheritance]s.
check(superClassSymbols.isNotEmpty())
}
}
/**
* The annotation class has unacceptable classifier as one of its parameters: not one of permitted classes ([String], [KClass]),
* primitives, etc. This may happen if the class representing this parameter was an annotation class before, but later it was
* converted to a non-annotation class.
*/
data class AnnotationWithUnacceptableParameter(
override val symbol: IrClassSymbol,
val unacceptableClassifierSymbol: IrClassifierSymbol
) : CanBeRootCause
/**
* The classifier depends on another unusable classifier. Thus, it is considered unusable too.
*/
data class DueToOtherClassifier(override val symbol: IrClassifierSymbol, val rootCause: CanBeRootCause) : Unusable
}
/** Indicates usable (fully linked) classifier. */
object Usable : ExploredClassifier
}
@@ -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.ir.overrides
package org.jetbrains.kotlin.ir.linkage.partial
import org.jetbrains.kotlin.descriptors.Modality
import org.jetbrains.kotlin.ir.declarations.IrClass
@@ -11,9 +11,9 @@ import org.jetbrains.kotlin.ir.declarations.IrDeclarationOrigin
import org.jetbrains.kotlin.ir.declarations.IrOverridableMember
interface IrUnimplementedOverridesStrategy {
class Customization(val origin: IrDeclarationOrigin?, val modality: Modality?, val needToCreateBody: Boolean) {
class Customization(val origin: IrDeclarationOrigin?, val modality: Modality?) {
companion object {
val NO = Customization(null, null, false)
val NO = Customization(null, null)
}
}
@@ -0,0 +1,154 @@
/*
* 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.ir.linkage.partial
import org.jetbrains.kotlin.ir.declarations.IrConstructor
import org.jetbrains.kotlin.ir.declarations.IrDeclaration
import org.jetbrains.kotlin.ir.declarations.IrOverridableDeclaration
import org.jetbrains.kotlin.ir.declarations.IrSimpleFunction
import org.jetbrains.kotlin.ir.expressions.*
import org.jetbrains.kotlin.ir.linkage.partial.PartialLinkageUtils.Module as PLModule
import org.jetbrains.kotlin.ir.symbols.*
/**
* Describes a reason why an [IrDeclaration] or an [IrExpression] is partially linked. Subclasses represent various causes of the p.l.
*/
@Suppress("KDocUnresolvedReference")
sealed interface PartialLinkageCase {
/**
* Unusable (partially linked) classifier.
*
* Applicable to: Declarations (classifiers).
*/
class UnusableClassifier(val cause: ExploredClassifier.Unusable.CanBeRootCause) : PartialLinkageCase
/**
* There is no real owner declaration for the symbol, only synthetic stub created by [MissingDeclarationStubGenerator].
* Likely the declaration has been deleted in newer version of the library.
*
* Applicable to: Declarations.
*/
class MissingDeclaration(val missingDeclarationSymbol: IrSymbol) : PartialLinkageCase
/**
* Declaration's signature uses an unusable (partially linked) classifier symbol.
*
* Applicable to: Declarations.
*/
class DeclarationWithUnusableClassifier(
val declarationSymbol: IrSymbol,
val cause: ExploredClassifier.Unusable
) : PartialLinkageCase
/**
* Expression uses an unusable (partially linked) classifier symbol.
* Example: An [IrTypeOperatorCall] that casts an argument to a type with unlinked symbol.
*
* Applicable to: Expressions.
*/
class ExpressionWithUnusableClassifier(
val expression: IrExpression,
val cause: ExploredClassifier.Unusable
) : PartialLinkageCase
/**
* Expression references a missing IR declaration (IR declaration)
* Example: An [IrCall] references unlinked [IrSimpleFunctionSymbol].
*
* Applicable to: Expressions.
*/
class ExpressionWithMissingDeclaration(
val expression: IrExpression,
val missingDeclarationSymbol: IrSymbol
) : PartialLinkageCase
/**
* Expression refers an IR declaration with a signature that uses an unusable (partially linked) classifier symbol.
*
* Applicable to: Expressions.
*/
class ExpressionHasDeclarationWithUnusableClassifier(
val expression: IrExpression,
val referencedDeclarationSymbol: IrSymbol,
val cause: ExploredClassifier.Unusable
) : PartialLinkageCase
/**
* Expression refers an IR declaration with the wrong type.
* Example: An [IrEnumConstructorCall] that refers an [IrConstructor] of a regular class.
*
* Applicable to: Expressions.
*/
class ExpressionHasWrongTypeOfDeclaration(
val expression: IrExpression,
val actualDeclarationSymbol: IrSymbol,
val expectedDeclarationDescription: String
) : PartialLinkageCase
/**
* Expression that refers to an IR function has an excessive or a missing dispatch receiver parameter,
* or the number of value arguments in expression does not match the number of value parameters in function
* (which may happen, for example, is a default value for a value parameter was removed).
*
* Applicable to: Expressions.
*/
class MemberAccessExpressionArgumentsMismatch(
val expression: IrMemberAccessExpression<IrFunctionSymbol>,
val expressionHasDispatchReceiver: Boolean,
val functionHasDispatchReceiver: Boolean,
val expressionValueArgumentCount: Int,
val functionValueParameterCount: Int
) : PartialLinkageCase
/**
* An [IrCall] of suspendable function at the place where no coroutine context is available.
*
* Applicable to: Expressions.
*/
class SuspendableFunctionCallWithoutCoroutineContext(val expression: IrCall) : PartialLinkageCase
/**
* A non-local return in context where it is not expected.
*
* Applicable to: Expressions.
*/
class IllegalNonLocalReturn(val expression: IrReturn, val validReturnTargets: Set<IrReturnTargetSymbol>) : PartialLinkageCase
/**
* Expression refers an IR declaration that is not accessible at the use site.
* Example: An [IrCall] that refers a private [IrSimpleFunction] from another module.
*
* Applicable to: Expressions.
*/
class ExpressionHasInaccessibleDeclaration(
val expression: IrExpression,
val referencedDeclarationSymbol: IrSymbol,
val declaringModule: PLModule,
val useSiteModule: PLModule
) : PartialLinkageCase
/**
* An [IrConstructor] delegates call to [unexpectedSuperClassConstructorSymbol] while should delegate to
* one of constructors of [superClassSymbol].
*/
class InvalidConstructorDelegation(
val constructorSymbol: IrConstructorSymbol,
val superClassSymbol: IrClassSymbol,
val unexpectedSuperClassConstructorSymbol: IrConstructorSymbol
) : PartialLinkageCase
/**
* An attempt to instantiate an abstract class from outside its inheritance hierarchy.
*/
class AbstractClassInstantiation(val constructorCall: IrConstructorCall, val classSymbol: IrClassSymbol) : PartialLinkageCase
/**
* Unimplemented abstract callable member in non-abstract class.
*
* Applicable to: Declarations (functions, properties).
*/
class UnimplementedAbstractCallable(val callable: IrOverridableDeclaration<*>) : PartialLinkageCase
}
@@ -0,0 +1,33 @@
/*
* 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.ir.linkage.partial
import org.jetbrains.kotlin.ir.IrElement
import org.jetbrains.kotlin.ir.expressions.IrCall
import org.jetbrains.kotlin.ir.linkage.partial.PartialLinkageUtils.File as PLFile
interface PartialLinkageSupportForLowerings {
val isEnabled: Boolean
fun throwLinkageError(
partialLinkageCase: PartialLinkageCase,
element: IrElement,
file: PLFile,
suppressWarningInCompilerOutput: Boolean
): IrCall
companion object {
val DISABLED = object : PartialLinkageSupportForLowerings {
override val isEnabled get() = false
override fun throwLinkageError(
partialLinkageCase: PartialLinkageCase,
element: IrElement,
file: PLFile,
suppressWarningInCompilerOutput: Boolean
): IrCall = error("Should not be called")
}
}
}
@@ -0,0 +1,136 @@
/*
* 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.ir.linkage.partial
import org.jetbrains.kotlin.builtins.FunctionInterfacePackageFragment
import org.jetbrains.kotlin.descriptors.PackageFragmentDescriptor
import org.jetbrains.kotlin.ir.*
import org.jetbrains.kotlin.ir.declarations.IrDeclaration
import org.jetbrains.kotlin.ir.declarations.IrDeclarationWithName
import org.jetbrains.kotlin.ir.declarations.IrExternalPackageFragment
import org.jetbrains.kotlin.ir.declarations.IrFile
import org.jetbrains.kotlin.ir.expressions.IrCall
import org.jetbrains.kotlin.ir.expressions.IrContainerExpression
import org.jetbrains.kotlin.ir.expressions.IrStatementOrigin.PARTIAL_LINKAGE_RUNTIME_ERROR
import org.jetbrains.kotlin.ir.util.IrMessageLogger
import org.jetbrains.kotlin.ir.util.getPackageFragment
import org.jetbrains.kotlin.name.Name
fun IrStatement.isPartialLinkageRuntimeError(): Boolean {
return when (this) {
is IrCall -> origin == PARTIAL_LINKAGE_RUNTIME_ERROR //|| symbol == builtIns.linkageErrorSymbol
is IrContainerExpression -> origin == PARTIAL_LINKAGE_RUNTIME_ERROR || statements.any { it.isPartialLinkageRuntimeError() }
else -> false
}
}
object PartialLinkageUtils {
/** For fast check if a declaration is in the module */
sealed interface Module {
val name: String
data class Real(override val name: String) : Module {
constructor(name: Name) : this(name.asString())
}
object SyntheticBuiltInFunctions : Module {
override val name = "<synthetic built-in functions>"
}
object MissingDeclarations : Module {
override val name = "<missing declarations>"
}
fun defaultLocationWithoutPath() = IrMessageLogger.Location(name, UNDEFINED_LINE_NUMBER, UNDEFINED_COLUMN_NUMBER)
companion object {
fun determineModuleFor(declaration: IrDeclaration): Module = determineFor(
declaration,
onMissingDeclaration = MissingDeclarations,
onSyntheticBuiltInFunction = SyntheticBuiltInFunctions,
onIrBased = { Real(it.module.name) },
onLazyIrBased = { Real(it.containingDeclaration.name) },
onError = { error("Can't determine module for $declaration, name=${(declaration as? IrDeclarationWithName)?.name}") }
)
}
}
sealed interface File {
val module: Module
fun computeLocationForOffset(offset: Int): IrMessageLogger.Location
data class IrBased(private val file: IrFile) : File {
override val module = Module.Real(file.module.name)
override fun computeLocationForOffset(offset: Int): IrMessageLogger.Location {
val lineNumber = if (offset == UNDEFINED_OFFSET) UNDEFINED_LINE_NUMBER else file.fileEntry.getLineNumber(offset) + 1 // since humans count from 1, not 0
val columnNumber = if (offset == UNDEFINED_OFFSET) UNDEFINED_COLUMN_NUMBER else file.fileEntry.getColumnNumber(offset) + 1
// TODO: should module name still be added here?
return IrMessageLogger.Location("${module.name} @ ${file.fileEntry.name}", lineNumber, columnNumber)
}
}
class LazyIrBased(packageFragmentDescriptor: PackageFragmentDescriptor) : File {
override val module = Module.Real(packageFragmentDescriptor.containingDeclaration.name)
private val defaultLocation = module.defaultLocationWithoutPath()
override fun equals(other: Any?) = (other as? LazyIrBased)?.module == module
override fun hashCode() = module.hashCode()
override fun computeLocationForOffset(offset: Int) = defaultLocation
}
object SyntheticBuiltInFunctions : File {
override val module = Module.SyntheticBuiltInFunctions
private val defaultLocation = module.defaultLocationWithoutPath()
override fun computeLocationForOffset(offset: Int) = defaultLocation
}
object MissingDeclarations : File {
override val module = Module.MissingDeclarations
private val defaultLocation = module.defaultLocationWithoutPath()
override fun computeLocationForOffset(offset: Int) = defaultLocation
}
companion object {
fun determineFileFor(declaration: IrDeclaration): File = determineFor(
declaration,
onMissingDeclaration = MissingDeclarations,
onSyntheticBuiltInFunction = SyntheticBuiltInFunctions,
onIrBased = ::IrBased,
onLazyIrBased = ::LazyIrBased,
onError = { error("Can't determine file for $declaration, name=${(declaration as? IrDeclarationWithName)?.name}") }
)
}
}
@OptIn(ObsoleteDescriptorBasedAPI::class)
private inline fun <R> determineFor(
declaration: IrDeclaration,
onMissingDeclaration: R,
onSyntheticBuiltInFunction: R,
onIrBased: (IrFile) -> R,
onLazyIrBased: (PackageFragmentDescriptor) -> R,
onError: () -> Nothing
): R {
return if (declaration.origin == PartiallyLinkedDeclarationOrigin.MISSING_DECLARATION)
onMissingDeclaration
else {
val packageFragment = declaration.getPackageFragment()
val packageFragmentDescriptor = with(packageFragment.symbol) { if (hasDescriptor) descriptor else null }
when {
packageFragmentDescriptor is FunctionInterfacePackageFragment -> onSyntheticBuiltInFunction
packageFragment is IrFile -> onIrBased(packageFragment)
packageFragment is IrExternalPackageFragment && packageFragmentDescriptor != null -> onLazyIrBased(packageFragmentDescriptor)
else -> onError()
}
}
}
}
@@ -0,0 +1,20 @@
/*
* 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.ir.linkage.partial
import org.jetbrains.kotlin.ir.declarations.IrDeclarationOrigin
@Suppress("KDocUnresolvedReference")
enum class PartiallyLinkedDeclarationOrigin : IrDeclarationOrigin {
/** The unresolved (missing) declaration */
MISSING_DECLARATION,
/** The abstract callable member that needs to be implemented in non-abstract class */
UNIMPLEMENTED_ABSTRACT_CALLABLE_MEMBER,
/** Auxiliary declaration generated by [PartiallyLinkedIrTreePatcher] */
AUXILIARY_GENERATED_DECLARATION;
}
@@ -9,6 +9,7 @@ import org.jetbrains.kotlin.ir.declarations.IrClass
import org.jetbrains.kotlin.ir.declarations.IrOverridableMember
import org.jetbrains.kotlin.ir.declarations.IrTypeParametersContainer
import org.jetbrains.kotlin.ir.expressions.IrConstructorCall
import org.jetbrains.kotlin.ir.linkage.partial.IrUnimplementedOverridesStrategy
import org.jetbrains.kotlin.ir.symbols.IrClassifierSymbol
import org.jetbrains.kotlin.ir.symbols.IrTypeParameterSymbol
import org.jetbrains.kotlin.ir.types.*
@@ -6,6 +6,7 @@
package org.jetbrains.kotlin.ir.overrides
import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.linkage.partial.IrUnimplementedOverridesStrategy
import org.jetbrains.kotlin.ir.util.*
class FakeOverrideCopier(
@@ -62,10 +63,6 @@ class FakeOverrideCopier(
extensionReceiverParameter = declaration.extensionReceiverParameter?.transform()
returnType = typeRemapper.remapType(declaration.returnType)
valueParameters = declaration.valueParameters.transform()
if (customization.needToCreateBody && body == null) {
body = factory.createBlockBody(startOffset, endOffset)
}
}
}
}
@@ -8,11 +8,12 @@ package org.jetbrains.kotlin.ir.overrides
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.ir.ObsoleteDescriptorBasedAPI
import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.linkage.partial.IrUnimplementedOverridesStrategy
import org.jetbrains.kotlin.ir.symbols.*
import org.jetbrains.kotlin.ir.types.*
import org.jetbrains.kotlin.ir.util.*
import org.jetbrains.kotlin.resolve.OverridingUtil.OverrideCompatibilityInfo
import org.jetbrains.kotlin.resolve.OverridingUtil.OverrideCompatibilityInfo.incompatible
import org.jetbrains.kotlin.resolve.OverridingUtil.OverrideCompatibilityInfo.*
import org.jetbrains.kotlin.types.AbstractTypeChecker
import org.jetbrains.kotlin.types.TypeCheckerState
import org.jetbrains.kotlin.types.Variance
@@ -32,8 +33,8 @@ abstract class FakeOverrideBuilderStrategy(
}
}
protected abstract fun linkFunctionFakeOverride(declaration: IrFunctionWithLateBinding, compatibilityMode: Boolean)
protected abstract fun linkPropertyFakeOverride(declaration: IrPropertyWithLateBinding, compatibilityMode: Boolean)
protected abstract fun linkFunctionFakeOverride(function: IrFunctionWithLateBinding, manglerCompatibleMode: Boolean)
protected abstract fun linkPropertyFakeOverride(property: IrPropertyWithLateBinding, manglerCompatibleMode: Boolean)
}
@OptIn(ObsoleteDescriptorBasedAPI::class) // Because of the LazyIR, have to use descriptors here.
@@ -223,7 +224,7 @@ class IrOverridingUtil(
for (fromCurrent in membersFromCurrent) {
val bound = extractAndBindOverridesForMember(fromCurrent, membersFromSupertypes)
notOverridden.removeAll(bound)
notOverridden -= bound
}
val addedFakeOverrides = mutableListOf<IrOverridableMember>()
@@ -237,30 +238,22 @@ class IrOverridingUtil(
): Collection<IrOverridableMember> {
val bound = ArrayList<IrOverridableMember>(descriptorsFromSuper.size)
val overridden = mutableSetOf<IrOverridableMember>()
for (fromSupertype in descriptorsFromSuper) {
val result = isOverridableBy(fromSupertype, fromCurrent/*, current*/).result
val isVisibleForOverride =
isVisibleForOverride(fromCurrent, fromSupertype.original)
when (result) {
// Note: We do allow overriding multiple FOs at once one of which is `isInline=true`.
when (isOverridableBy(fromSupertype, fromCurrent, checkIsInlineFlag = true, checkReturnType = false).result) {
OverrideCompatibilityInfo.Result.OVERRIDABLE -> {
if (isVisibleForOverride) {
overridden.add(fromSupertype)
}
bound.add(fromSupertype)
if (isVisibleForOverride(fromCurrent, fromSupertype.original))
overridden += fromSupertype
bound += fromSupertype
}
OverrideCompatibilityInfo.Result.CONFLICT -> {
// if (isVisibleForOverride) {
// strategy.overrideConflict(fromSupertype, fromCurrent)
// }
// Do nothing.
bound.add(fromSupertype)
}
OverrideCompatibilityInfo.Result.INCOMPATIBLE -> {
bound += fromSupertype
}
OverrideCompatibilityInfo.Result.INCOMPATIBLE -> Unit
}
}
//strategy.setOverriddenDescriptors(fromCurrent, overridden)
fromCurrent.overriddenSymbols = overridden.map { it.original.symbol }
return bound
@@ -404,7 +397,7 @@ class IrOverridingUtil(
private fun createAndBindFakeOverride(
overridables: Collection<IrOverridableMember>,
current: IrClass,
currentClass: IrClass,
addedFakeOverrides: MutableList<IrOverridableMember>,
compatibilityMode: Boolean
) {
@@ -414,7 +407,7 @@ class IrOverridingUtil(
// but we don't use invisible fakes in IR
if (effectiveOverridden.isEmpty()) return
val modality = determineModalityForFakeOverride(effectiveOverridden, current)
val modality = determineModalityForFakeOverride(effectiveOverridden, currentClass)
val visibility = findMemberWithMaxVisibility(effectiveOverridden).visibility
val mostSpecific = selectMostSpecificMember(effectiveOverridden)
@@ -600,120 +593,103 @@ class IrOverridingUtil(
private fun getBothWaysOverridability(
overriderDescriptor: IrOverridableMember,
candidateDescriptor: IrOverridableMember
): OverrideCompatibilityInfo.Result {
): Result {
val result1 = isOverridableBy(
candidateDescriptor,
overriderDescriptor
//null
overriderDescriptor,
checkIsInlineFlag = false,
checkReturnType = false
).result
val result2 = isOverridableBy(
overriderDescriptor,
candidateDescriptor
//null
candidateDescriptor,
checkIsInlineFlag = false,
checkReturnType = false
).result
return if (result1 == OverrideCompatibilityInfo.Result.OVERRIDABLE && result2 == OverrideCompatibilityInfo.Result.OVERRIDABLE)
OverrideCompatibilityInfo.Result.OVERRIDABLE
else if (result1 == OverrideCompatibilityInfo.Result.CONFLICT || result2 == OverrideCompatibilityInfo.Result.CONFLICT)
OverrideCompatibilityInfo.Result.CONFLICT
else
OverrideCompatibilityInfo.Result.INCOMPATIBLE
return if (result1 == result2) result1 else OverrideCompatibilityInfo.Result.INCOMPATIBLE
}
private fun isOverridableBy(
superMember: IrOverridableMember,
subMember: IrOverridableMember,
// subClass: IrClass?
): OverrideCompatibilityInfo {
return isOverridableBy(superMember, subMember/*, subClass*/, false)
}
private fun isOverridableBy(
superMember: IrOverridableMember,
subMember: IrOverridableMember,
// subClass: IrClass?, Would only be needed for external overridability conditions.
checkIsInlineFlag: Boolean,
checkReturnType: Boolean
): OverrideCompatibilityInfo {
val basicResult = isOverridableByWithoutExternalConditions(superMember, subMember, checkReturnType)
return if (basicResult.result == OverrideCompatibilityInfo.Result.OVERRIDABLE)
OverrideCompatibilityInfo.success()
else
basicResult
return isOverridableByWithoutExternalConditions(superMember, subMember, checkIsInlineFlag, checkReturnType)
// The frontend goes into external overridability condition details here, but don't deal with them in IR (yet?).
}
private val IrOverridableMember.compiledValueParameters
get() = when (this) {
is IrSimpleFunction -> extensionReceiverParameter?.let { listOf(it) + valueParameters } ?: valueParameters
is IrProperty -> getter!!.extensionReceiverParameter?.let { listOf(it) } ?: emptyList()
else -> error("Unexpected declaration for compiledValueParameters: $this")
}
private val IrOverridableMember.returnType
get() = when (this) {
is IrSimpleFunction -> this.returnType
is IrProperty -> this.getter!!.returnType
else -> error("Unexpected declaration for returnType: $this")
}
private val IrOverridableMember.typeParameters
get() = when (this) {
is IrSimpleFunction -> this.typeParameters
is IrProperty -> this.getter!!.typeParameters
else -> error("Unexpected declaration for typeParameters: $this")
}
private fun isOverridableByWithoutExternalConditions(
superMember: IrOverridableMember,
subMember: IrOverridableMember,
checkIsInlineFlag: Boolean,
checkReturnType: Boolean
): OverrideCompatibilityInfo {
val basicOverridability = getBasicOverridabilityProblem(superMember, subMember)
if (basicOverridability != null) return basicOverridability
val superTypeParameters: List<IrTypeParameter>
val subTypeParameters: List<IrTypeParameter>
val superValueParameters = superMember.compiledValueParameters
val subValueParameters = subMember.compiledValueParameters
val superTypeParameters = superMember.typeParameters
val subTypeParameters = subMember.typeParameters
val superValueParameters: List<IrValueParameter>
val subValueParameters: List<IrValueParameter>
if (superTypeParameters.size != subTypeParameters.size) {
/* TODO: do we need this in IR?
superValueParameters.forEachIndexed { index, superParameter ->
if (!AbstractTypeChecker.equalTypes(
defaultTypeCheckerContext as AbstractTypeCheckerContext,
superParameter.type,
subValueParameters[index].type
)
) {
return OverrideCompatibilityInfo.incompatible("Type parameter number mismatch")
when (superMember) {
is IrSimpleFunction -> when {
subMember !is IrSimpleFunction -> return incompatible("Member kind mismatch")
superMember.hasExtensionReceiver != subMember.hasExtensionReceiver -> return incompatible("Receiver presence mismatch")
superMember.isSuspend != subMember.isSuspend -> return incompatible("Incompatible suspendability")
checkIsInlineFlag && superMember.isInline -> return incompatible("Inline function can't be overridden")
else -> {
superTypeParameters = superMember.typeParameters
subTypeParameters = subMember.typeParameters
superValueParameters = superMember.compiledValueParameters
subValueParameters = subMember.compiledValueParameters
}
}
return OverrideCompatibilityInfo.conflict("Type parameter number mismatch")
*/
is IrProperty -> when {
subMember !is IrProperty -> return incompatible("Member kind mismatch")
superMember.getter.hasExtensionReceiver != subMember.getter.hasExtensionReceiver -> return incompatible("Receiver presence mismatch")
checkIsInlineFlag && superMember.isInline -> return incompatible("Inline property can't be overridden")
return incompatible("Type parameter number mismatch")
else -> {
superTypeParameters = superMember.typeParameters
subTypeParameters = subMember.typeParameters
superValueParameters = superMember.compiledValueParameters
subValueParameters = subMember.compiledValueParameters
}
}
else -> error("Unexpected type of declaration: ${superMember::class.java}, $superMember")
}
val typeCheckerState =
createIrTypeCheckerState(
IrTypeSystemContextWithAdditionalAxioms(
typeSystem,
superTypeParameters,
subTypeParameters
)
when {
superMember.name != subMember.name -> {
// Check name after member kind checks. This way FO builder will first check types of overridable members and crash
// if member types are not supported (ex: IrConstructor).
return incompatible("Name mismatch")
}
superTypeParameters.size != subTypeParameters.size -> return incompatible("Type parameter number mismatch")
superValueParameters.size != subValueParameters.size -> return incompatible("Value parameter number mismatch")
}
// TODO: check the bounds. See OverridingUtil.areTypeParametersEquivalent()
// superTypeParameters.forEachIndexed { index, parameter ->
// if (!AbstractTypeChecker.areTypeParametersEquivalent(
// typeCheckerContext as AbstractTypeCheckerContext,
// subTypeParameters[index].type,
// parameter.type
// )
// ) return OverrideCompatibilityInfo.incompatible("Type parameter bounds mismatch")
// }
val typeCheckerState = createIrTypeCheckerState(
IrTypeSystemContextWithAdditionalAxioms(
typeSystem,
superTypeParameters,
subTypeParameters
)
/* TODO: check the bounds. See OverridingUtil.areTypeParametersEquivalent()
superTypeParameters.forEachIndexed { index, parameter ->
if (!AbstractTypeChecker.areTypeParametersEquivalent(
typeCheckerContext as AbstractTypeCheckerContext,
subTypeParameters[index].type,
parameter.type
)
) return OverrideCompatibilityInfo.incompatible("Type parameter bounds mismatch")
}
*/
require(superValueParameters.size == subValueParameters.size)
)
superValueParameters.forEachIndexed { index, parameter ->
if (!AbstractTypeChecker.equalTypes(
@@ -724,76 +700,59 @@ class IrOverridingUtil(
) return incompatible("Value parameter type mismatch")
}
if (superMember is IrSimpleFunction && subMember is IrSimpleFunction && superMember.isSuspend != subMember.isSuspend) {
return OverrideCompatibilityInfo.conflict("Incompatible suspendability")
}
if (checkReturnType) {
if (!AbstractTypeChecker.isSubtypeOf(
typeCheckerState,
subMember.returnType,
superMember.returnType
)
) return OverrideCompatibilityInfo.conflict("Return type mismatch")
}
return OverrideCompatibilityInfo.success()
}
private fun getBasicOverridabilityProblem(
superMember: IrOverridableMember,
subMember: IrOverridableMember
): OverrideCompatibilityInfo? {
if (superMember is IrSimpleFunction && subMember !is IrSimpleFunction ||
superMember is IrProperty && subMember !is IrProperty
) {
return incompatible("Member kind mismatch")
}
require((superMember is IrSimpleFunction || superMember is IrProperty)) {
"This type of IrDeclaration cannot be checked for overridability: $superMember"
) return conflict("Return type mismatch")
}
return if (superMember.name != subMember.name) {
incompatible("Name mismatch")
} else
checkReceiverAndParameterCount(superMember, subMember)
}
private fun checkReceiverAndParameterCount(
superMember: IrOverridableMember,
subMember: IrOverridableMember
): OverrideCompatibilityInfo? {
return when (superMember) {
is IrSimpleFunction -> {
require(subMember is IrSimpleFunction)
when {
superMember.extensionReceiverParameter == null != (subMember.extensionReceiverParameter == null) -> {
incompatible("Receiver presence mismatch")
}
superMember.valueParameters.size != subMember.valueParameters.size -> {
incompatible("Value parameter number mismatch")
}
else -> null
}
}
is IrProperty -> {
require(subMember is IrProperty)
if (superMember.getter?.extensionReceiverParameter == null != (subMember.getter?.extensionReceiverParameter == null)) {
incompatible("Receiver presence mismatch")
} else null
}
else -> error("Unxpected declaration for value parameter check: $this")
}
return success()
}
}
private val IrSimpleFunction?.hasExtensionReceiver: Boolean
get() = this?.extensionReceiverParameter != null
private val IrSimpleFunction?.hasDispatchReceiver: Boolean
get() = this?.dispatchReceiverParameter != null
private val IrSimpleFunction.compiledValueParameters: List<IrValueParameter>
get() = ArrayList<IrValueParameter>(valueParameters.size + 1).apply {
extensionReceiverParameter?.let(::add)
addAll(valueParameters)
}
private val IrProperty.compiledValueParameters: List<IrValueParameter>
get() = getter?.extensionReceiverParameter?.let(::listOf).orEmpty()
private val IrProperty.typeParameters: List<IrTypeParameter>
get() = getter?.typeParameters.orEmpty()
private val IrProperty.isInline: Boolean
get() = getter?.isInline == true || setter?.isInline == true
private val IrOverridableMember.typeParameters: List<IrTypeParameter>
get() = when (this) {
is IrSimpleFunction -> typeParameters
is IrProperty -> getter?.typeParameters.orEmpty()
else -> error("Unexpected type of declaration: ${this::class.java}, $this")
}
private val IrOverridableMember.returnType
get() = when (this) {
is IrSimpleFunction -> returnType
is IrProperty -> getter!!.returnType
else -> error("Unexpected type of declaration: ${this::class.java}, $this")
}
fun IrSimpleFunction.isOverridableFunction(): Boolean =
this.visibility != DescriptorVisibilities.PRIVATE &&
this.dispatchReceiverParameter != null
visibility != DescriptorVisibilities.PRIVATE && hasDispatchReceiver
fun IrProperty.isOverridableProperty(): Boolean =
this.visibility != DescriptorVisibilities.PRIVATE &&
(this.getter?.dispatchReceiverParameter != null ||
this.setter?.dispatchReceiverParameter != null)
visibility != DescriptorVisibilities.PRIVATE && (getter.hasDispatchReceiver || setter.hasDispatchReceiver)
fun IrDeclaration.isOverridableMemberOrAccessor(): Boolean = when (this) {
is IrSimpleFunction -> isOverridableFunction()
@@ -5,9 +5,11 @@
package org.jetbrains.kotlin.ir.overrides
import org.jetbrains.kotlin.descriptors.DescriptorVisibilities
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.ir.declarations.IrDeclarationWithVisibility
import org.jetbrains.kotlin.ir.declarations.IrOverridableDeclaration
import org.jetbrains.kotlin.ir.declarations.IrOverridableMember
import org.jetbrains.kotlin.ir.util.parentClassOrNull
// The contents of this file is from VisibilityUtil.kt adapted to IR.
// TODO: The code would better be commonized for descriptors, ir and fir.
@@ -36,3 +38,25 @@ fun findMemberWithMaxVisibility(members: Collection<IrOverridableMember>): IrOve
}
return member ?: error("Could not find a visible member")
}
fun IrDeclarationWithVisibility.isEffectivelyPrivate(): Boolean {
fun DescriptorVisibility.isNonPrivate(): Boolean =
this == DescriptorVisibilities.PUBLIC
|| this == DescriptorVisibilities.PROTECTED
|| this == DescriptorVisibilities.INTERNAL
return when {
visibility.isNonPrivate() -> parentClassOrNull?.isEffectivelyPrivate() ?: false
visibility == DescriptorVisibilities.INVISIBLE_FAKE -> {
val overridesOnlyPrivateDeclarations = (this as? IrOverridableDeclaration<*>)
?.overriddenSymbols
?.all { (it.owner as? IrDeclarationWithVisibility)?.isEffectivelyPrivate() == true }
?: false
overridesOnlyPrivateDeclarations || (parentClassOrNull?.isEffectivelyPrivate() ?: false)
}
else -> true
}
}
@@ -156,15 +156,12 @@ abstract class ConstantValueGenerator(
@OptIn(ObsoleteDescriptorBasedAPI::class)
fun generateAnnotationConstructorCall(annotationDescriptor: AnnotationDescriptor, realType: KotlinType? = null): IrConstructorCall? {
val annotationType = realType ?: annotationDescriptor.type
val annotationClassDescriptor = annotationType.constructor.declarationDescriptor
if (annotationClassDescriptor !is ClassDescriptor) return null
if (annotationClassDescriptor is NotFoundClasses.MockClassDescriptor) return null
val annotationClassDescriptor = annotationType.constructor.declarationDescriptor as? ClassDescriptor ?: return null
assert(
DescriptorUtils.isAnnotationClass(annotationClassDescriptor) ||
(allowErrorTypeInAnnotations && annotationClassDescriptor is ErrorClassDescriptor)
) {
"Annotation class expected: $annotationClassDescriptor"
when (annotationClassDescriptor) {
is NotFoundClasses.MockClassDescriptor -> return null
is ErrorClassDescriptor -> if (!allowErrorTypeInAnnotations) return null
else -> if (!DescriptorUtils.isAnnotationClass(annotationClassDescriptor)) return null
}
val primaryConstructorDescriptor = annotationClassDescriptor.unsubstitutedPrimaryConstructor
@@ -14,9 +14,9 @@ import org.jetbrains.kotlin.ir.builders.declarations.buildTypeParameter
import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.expressions.*
import org.jetbrains.kotlin.ir.expressions.impl.*
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.ProcessAsFakeOverrides
import org.jetbrains.kotlin.ir.symbols.*
import org.jetbrains.kotlin.ir.symbols.impl.IrPropertySymbolImpl
import org.jetbrains.kotlin.ir.symbols.impl.IrSimpleFunctionSymbolImpl
@@ -1139,24 +1139,24 @@ private class FakeOverrideBuilderForLowerings : FakeOverrideBuilderStrategy(
friendModules = emptyMap(),
unimplementedOverridesStrategy = ProcessAsFakeOverrides
) {
override fun linkFunctionFakeOverride(declaration: IrFunctionWithLateBinding, compatibilityMode: Boolean) {
declaration.acquireSymbol(IrSimpleFunctionSymbolImpl())
override fun linkFunctionFakeOverride(function: IrFunctionWithLateBinding, manglerCompatibleMode: Boolean) {
function.acquireSymbol(IrSimpleFunctionSymbolImpl())
}
override fun linkPropertyFakeOverride(declaration: IrPropertyWithLateBinding, compatibilityMode: Boolean) {
override fun linkPropertyFakeOverride(property: IrPropertyWithLateBinding, manglerCompatibleMode: Boolean) {
val propertySymbol = IrPropertySymbolImpl()
declaration.getter?.let { it.correspondingPropertySymbol = propertySymbol }
declaration.setter?.let { it.correspondingPropertySymbol = propertySymbol }
property.getter?.let { it.correspondingPropertySymbol = propertySymbol }
property.setter?.let { it.correspondingPropertySymbol = propertySymbol }
declaration.acquireSymbol(propertySymbol)
property.acquireSymbol(propertySymbol)
declaration.getter?.let {
it.correspondingPropertySymbol = declaration.symbol
linkFunctionFakeOverride(it as? IrFunctionWithLateBinding ?: error("Unexpected fake override getter: $it"), compatibilityMode)
property.getter?.let {
it.correspondingPropertySymbol = property.symbol
linkFunctionFakeOverride(it as? IrFunctionWithLateBinding ?: error("Unexpected fake override getter: $it"), manglerCompatibleMode)
}
declaration.setter?.let {
it.correspondingPropertySymbol = declaration.symbol
linkFunctionFakeOverride(it as? IrFunctionWithLateBinding ?: error("Unexpected fake override setter: $it"), compatibilityMode)
property.setter?.let {
it.correspondingPropertySymbol = property.symbol
linkFunctionFakeOverride(it as? IrFunctionWithLateBinding ?: error("Unexpected fake override setter: $it"), manglerCompatibleMode)
}
}
}
@@ -1329,8 +1329,8 @@ private fun IrSimpleFunction.copyAndRenameConflictingTypeParametersFrom(
val IrSymbol.isSuspend: Boolean
get() = this is IrSimpleFunctionSymbol && owner.isSuspend
fun IrSimpleFunction.allOverridden(includeSelf: Boolean = false): List<IrSimpleFunction> {
val result = mutableListOf<IrSimpleFunction>()
fun <T : IrOverridableDeclaration<*>> T.allOverridden(includeSelf: Boolean = false): List<T> {
val result = mutableListOf<T>()
if (includeSelf) {
result.add(this)
}
@@ -1341,7 +1341,8 @@ fun IrSimpleFunction.allOverridden(includeSelf: Boolean = false): List<IrSimpleF
when (overridden.size) {
0 -> return result
1 -> {
current = overridden[0].owner
@Suppress("UNCHECKED_CAST")
current = overridden[0].owner as T
result.add(current)
}
else -> {
@@ -1353,9 +1354,9 @@ fun IrSimpleFunction.allOverridden(includeSelf: Boolean = false): List<IrSimpleF
}
}
private fun computeAllOverridden(function: IrSimpleFunction, result: MutableSet<IrSimpleFunction>) {
for (overriddenSymbol in function.overriddenSymbols) {
val override = overriddenSymbol.owner
private fun <T : IrOverridableDeclaration<*>> computeAllOverridden(overridable: T, result: MutableSet<T>) {
for (overriddenSymbol in overridable.overriddenSymbols) {
@Suppress("UNCHECKED_CAST") val override = overriddenSymbol.owner as T
if (result.add(override)) {
computeAllOverridden(override, result)
}
@@ -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
}
}
@@ -19,9 +19,9 @@ import org.jetbrains.kotlin.backend.common.CommonJsKLibResolver
import org.jetbrains.kotlin.backend.common.extensions.IrGenerationExtension
import org.jetbrains.kotlin.backend.common.extensions.IrPluginContext
import org.jetbrains.kotlin.backend.common.extensions.IrPluginContextImpl
import org.jetbrains.kotlin.backend.common.linkage.issues.checkNoUnboundSymbols
import org.jetbrains.kotlin.backend.common.overrides.FakeOverrideChecker
import org.jetbrains.kotlin.backend.common.serialization.*
import org.jetbrains.kotlin.backend.common.serialization.linkerissues.checkNoUnboundSymbols
import org.jetbrains.kotlin.backend.common.serialization.mangle.ManglerChecker
import org.jetbrains.kotlin.backend.common.serialization.mangle.descriptor.Ir2DescriptorManglerAdapter
import org.jetbrains.kotlin.backend.common.serialization.metadata.*
@@ -45,7 +45,7 @@ class JsIrLinker(
mangler = JsManglerIr,
typeSystem = IrTypeSystemContextImpl(builtIns),
friendModules = friendModules,
partialLinkageEnabled = partialLinkageSupport.partialLinkageEnabled
partialLinkageEnabled = partialLinkageSupport.isEnabled
)
override fun isBuiltInModule(moduleDescriptor: ModuleDescriptor): Boolean =
@@ -85,6 +85,7 @@ fun deserializeFromByteArray(
DefaultFakeOverrideClassFilter,
fakeOverrideBuilder,
compatibilityMode = CompatibilityMode.CURRENT,
partialLinkageEnabled = false
)
for (declarationProto in irProto.declarationList) {
deserializer.deserializeDeclaration(declarationProto, setParent = false)
@@ -17,8 +17,8 @@
package org.jetbrains.kotlin.ir
import org.jetbrains.kotlin.analyzer.AnalysisResult
import org.jetbrains.kotlin.backend.common.linkage.issues.checkNoUnboundSymbols
import org.jetbrains.kotlin.backend.common.serialization.DescriptorByIdSignatureFinderImpl
import org.jetbrains.kotlin.backend.common.serialization.linkerissues.checkNoUnboundSymbols
import org.jetbrains.kotlin.backend.common.serialization.signature.IdSignatureDescriptor
import org.jetbrains.kotlin.backend.jvm.JvmGeneratorExtensionsImpl
import org.jetbrains.kotlin.backend.jvm.serialization.JvmIdSignatureDescriptor
@@ -9,9 +9,9 @@ import com.intellij.openapi.project.Project
import com.intellij.psi.PsiElement
import junit.framework.TestCase
import org.jetbrains.kotlin.backend.common.CommonJsKLibResolver
import org.jetbrains.kotlin.backend.common.linkage.issues.checkNoUnboundSymbols
import org.jetbrains.kotlin.backend.common.serialization.CompatibilityMode
import org.jetbrains.kotlin.backend.common.serialization.DeserializationStrategy
import org.jetbrains.kotlin.backend.common.serialization.linkerissues.checkNoUnboundSymbols
import org.jetbrains.kotlin.backend.common.serialization.metadata.KlibMetadataIncrementalSerializer
import org.jetbrains.kotlin.backend.common.serialization.metadata.makeSerializedKlibMetadata
import org.jetbrains.kotlin.backend.common.serialization.signature.IdSignatureDescriptor
@@ -13,10 +13,10 @@ import com.intellij.openapi.vfs.VirtualFileManager
import com.intellij.psi.PsiManager
import org.jetbrains.kotlin.KtPsiSourceFile
import org.jetbrains.kotlin.analyzer.AnalysisResult
import org.jetbrains.kotlin.backend.common.linkage.issues.checkNoUnboundSymbols
import org.jetbrains.kotlin.backend.common.phaser.PhaseConfig
import org.jetbrains.kotlin.backend.common.phaser.invokeToplevel
import org.jetbrains.kotlin.backend.common.serialization.CompatibilityMode
import org.jetbrains.kotlin.backend.common.serialization.linkerissues.checkNoUnboundSymbols
import org.jetbrains.kotlin.backend.common.serialization.signature.IdSignatureDescriptor
import org.jetbrains.kotlin.build.report.DoNothingBuildReporter
import org.jetbrains.kotlin.cli.common.arguments.K2JSCompilerArguments
@@ -9,6 +9,7 @@ import llvm.LLVMTypeRef
import org.jetbrains.kotlin.backend.common.DefaultDelegateFactory
import org.jetbrains.kotlin.backend.common.DefaultMapping
import org.jetbrains.kotlin.backend.common.LoggingContext
import org.jetbrains.kotlin.backend.common.linkage.partial.createPartialLinkageSupportForLowerings
import org.jetbrains.kotlin.backend.konan.cexport.CAdapterExportedElements
import org.jetbrains.kotlin.backend.konan.descriptors.BridgeDirections
import org.jetbrains.kotlin.backend.konan.descriptors.ClassLayoutBuilder
@@ -27,6 +28,7 @@ import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.symbols.IrFunctionSymbol
import org.jetbrains.kotlin.ir.types.IrTypeSystemContext
import org.jetbrains.kotlin.ir.types.IrTypeSystemContextImpl
import org.jetbrains.kotlin.ir.util.irMessageLogger
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.resolve.descriptorUtil.module
@@ -111,6 +113,12 @@ internal class Context(
val memoryModel = config.memoryModel
override fun dispose() {}
override val partialLinkageSupport = createPartialLinkageSupportForLowerings(
config.partialLinkageEnabled,
irBuiltIns,
configuration.irMessageLogger
)
}
internal class ContextLogger(val context: LoggingContext) {
@@ -6,16 +6,11 @@
package org.jetbrains.kotlin.backend.konan
import org.jetbrains.kotlin.backend.common.CommonBackendContext
import org.jetbrains.kotlin.backend.common.DefaultMapping
import org.jetbrains.kotlin.backend.common.Mapping
import org.jetbrains.kotlin.backend.konan.descriptors.KonanSharedVariablesManager
import org.jetbrains.kotlin.backend.konan.driver.BasicPhaseContext
import org.jetbrains.kotlin.backend.konan.ir.KonanIr
import org.jetbrains.kotlin.builtins.konan.KonanBuiltIns
import org.jetbrains.kotlin.cli.common.CLIConfigurationKeys
import org.jetbrains.kotlin.cli.common.messages.CompilerMessageLocation
import org.jetbrains.kotlin.cli.common.messages.CompilerMessageSeverity
import org.jetbrains.kotlin.cli.common.messages.MessageCollector
import org.jetbrains.kotlin.ir.IrElement
import org.jetbrains.kotlin.ir.builders.IrBuilderWithScope
import org.jetbrains.kotlin.ir.declarations.IrDeclaration
@@ -7,7 +7,7 @@ package org.jetbrains.kotlin.backend.konan
import com.google.common.io.Files
import com.intellij.openapi.project.Project
import org.jetbrains.kotlin.backend.common.serialization.linkerissues.UserVisibleIrModulesSupport
import org.jetbrains.kotlin.backend.common.linkage.issues.UserVisibleIrModulesSupport
import org.jetbrains.kotlin.backend.konan.serialization.KonanUserVisibleIrModulesSupport
import org.jetbrains.kotlin.cli.common.CLIConfigurationKeys
import org.jetbrains.kotlin.cli.common.config.kotlinSourceRoots
@@ -399,7 +399,7 @@ class KonanConfig(val project: Project, val configuration: CompilerConfiguration
internal val useDebugInfoInNativeLibs= configuration.get(BinaryOptions.stripDebugInfoFromNativeLibs) == false
internal val partialLinkage = configuration.get(KonanConfigKeys.PARTIAL_LINKAGE) == true
internal val partialLinkageEnabled = configuration[KonanConfigKeys.PARTIAL_LINKAGE] ?: false
internal val additionalCacheFlags by lazy { platformManager.loader(target).additionalCacheFlags }
@@ -433,8 +433,7 @@ class KonanConfig(val project: Project, val configuration: CompilerConfiguration
private val userCacheFlavorString = buildString {
appendCommonCacheFlavor()
if (partialLinkage) append("-pl")
if (partialLinkageEnabled) append("-pl")
}
private val systemCacheRootDirectory = File(distribution.konanHome).child("klib").child("cache")
@@ -2,9 +2,9 @@ package org.jetbrains.kotlin.backend.konan
import org.jetbrains.kotlin.backend.common.extensions.IrGenerationExtension
import org.jetbrains.kotlin.backend.common.extensions.IrPluginContextImpl
import org.jetbrains.kotlin.backend.common.linkage.issues.checkNoUnboundSymbols
import org.jetbrains.kotlin.backend.common.overrides.FakeOverrideChecker
import org.jetbrains.kotlin.backend.common.serialization.DescriptorByIdSignatureFinderImpl
import org.jetbrains.kotlin.backend.common.serialization.linkerissues.checkNoUnboundSymbols
import org.jetbrains.kotlin.backend.common.serialization.mangle.ManglerChecker
import org.jetbrains.kotlin.backend.common.serialization.mangle.descriptor.Ir2DescriptorManglerAdapter
import org.jetbrains.kotlin.backend.konan.descriptors.isForwardDeclarationModule
@@ -51,7 +51,7 @@ internal fun PsiToIrContext.psiToIr(
val (moduleDescriptor, environment, isProducingLibrary) = input
// Translate AST to high level IR.
val expectActualLinker = config.configuration[CommonConfigurationKeys.EXPECT_ACTUAL_LINKER] ?: false
val messageLogger = config.configuration[IrMessageLogger.IR_MESSAGE_LOGGER] ?: IrMessageLogger.None
val messageLogger = config.configuration.irMessageLogger
val partialLinkageEnabled = config.configuration[KonanConfigKeys.PARTIAL_LINKAGE] ?: false
@@ -284,7 +284,7 @@ private fun String.absoluteNormalizedFile() = java.io.File(this).absoluteFile.no
internal fun CompilerConfiguration.setupCommonOptionsForCaches(konanConfig: KonanConfig) = with(KonanConfigKeys) {
put(TARGET, konanConfig.target.toString())
put(DEBUG, konanConfig.debug)
put(PARTIAL_LINKAGE, konanConfig.partialLinkage)
put(PARTIAL_LINKAGE, konanConfig.partialLinkageEnabled)
putIfNotNull(EXTERNAL_DEPENDENCIES, konanConfig.externalDependenciesFile?.absolutePath)
put(BinaryOptions.memoryModel, konanConfig.memoryModel)
put(PROPERTY_LAZY_INITIALIZATION, konanConfig.propertyLazyInitialization)
@@ -8,7 +8,8 @@ package org.jetbrains.kotlin.backend.konan.lower
import org.jetbrains.kotlin.backend.common.ClassLoweringPass
import org.jetbrains.kotlin.backend.common.IrElementTransformerVoidWithContext
import org.jetbrains.kotlin.backend.common.getOrPut
import org.jetbrains.kotlin.backend.common.lower.callsSuper
import org.jetbrains.kotlin.backend.common.lower.ConstructorDelegationKind
import org.jetbrains.kotlin.backend.common.lower.delegationKind
import org.jetbrains.kotlin.backend.konan.Context
import org.jetbrains.kotlin.backend.konan.NativeMapping
import org.jetbrains.kotlin.backend.konan.descriptors.synthesizedName
@@ -175,7 +176,7 @@ internal class InnerClassLowering(val context: Context) : ClassLoweringPass {
}
private fun lowerConstructor(irConstructor: IrConstructor): IrConstructor {
if (irConstructor.callsSuper(context.irBuiltIns)) {
if (irConstructor.delegationKind(context.irBuiltIns) == ConstructorDelegationKind.CALLS_SUPER) {
// Initializing constructor: initialize 'this.this$0' with '$outer'.
val blockBody = irConstructor.body as? IrBlockBody
?: throw AssertionError("Unexpected constructor body: ${irConstructor.body}")
@@ -7,24 +7,17 @@ package org.jetbrains.kotlin.backend.konan.lower
import org.jetbrains.kotlin.backend.common.lower.coroutines.AbstractAddContinuationToFunctionCallsLowering
import org.jetbrains.kotlin.backend.konan.Context
import org.jetbrains.kotlin.ir.declarations.IrDeclarationOrigin
import org.jetbrains.kotlin.ir.declarations.IrSimpleFunction
import org.jetbrains.kotlin.ir.util.overrides
internal class NativeAddContinuationToFunctionCallsLowering(override val context: Context) : AbstractAddContinuationToFunctionCallsLowering() {
internal class NativeAddContinuationToFunctionCallsLowering(
override val context: Context
) : AbstractAddContinuationToFunctionCallsLowering() {
/*
* In complex cases suspend functions are converted to state-machine class with invokeSuspend method.
* In that case continuation is an object itself
* In simple cases, function is left as is, and receives continuation as its last parameter
* We should handle both cases here
*/
override fun IrSimpleFunction.getContinuationParameter() = when {
overrides(context.ir.symbols.invokeSuspendFunction.owner) -> dispatchReceiverParameter!!
else -> {
valueParameters.lastOrNull().also {
require(origin == IrDeclarationOrigin.LOWERED_SUSPEND_FUNCTION) { "Continuation parameter only exists in lowered suspend functions, but function origin is $origin" }
require(it != null && it.origin == IrDeclarationOrigin.CONTINUATION) { "Continuation parameter is expected to be last one" }
}!!
}
}
}
override fun IrSimpleFunction.isContinuationItself(): Boolean = overrides(context.ir.symbols.invokeSuspendFunction.owner)
}
@@ -16,6 +16,8 @@
package org.jetbrains.kotlin.backend.konan.serialization
import org.jetbrains.kotlin.backend.common.linkage.issues.UserVisibleIrModulesSupport
import org.jetbrains.kotlin.backend.common.linkage.issues.checkNoUnboundSymbols
import org.jetbrains.kotlin.backend.common.lower.parents
import org.jetbrains.kotlin.backend.common.overrides.FakeOverrideBuilder
import org.jetbrains.kotlin.backend.common.overrides.FakeOverrideClassFilter
@@ -23,17 +25,13 @@ import org.jetbrains.kotlin.backend.common.serialization.*
import org.jetbrains.kotlin.backend.common.serialization.encodings.BinaryNameAndType
import org.jetbrains.kotlin.backend.common.serialization.encodings.BinarySymbolData
import org.jetbrains.kotlin.backend.common.serialization.encodings.FunctionFlags
import org.jetbrains.kotlin.backend.common.serialization.isForwardDeclarationModule
import org.jetbrains.kotlin.backend.common.serialization.linkerissues.UserVisibleIrModulesSupport
import org.jetbrains.kotlin.backend.konan.*
import org.jetbrains.kotlin.backend.konan.descriptors.*
import org.jetbrains.kotlin.backend.konan.descriptors.ClassLayoutBuilder
import org.jetbrains.kotlin.backend.konan.descriptors.findPackage
import org.jetbrains.kotlin.backend.konan.descriptors.isFromInteropLibrary
import org.jetbrains.kotlin.backend.konan.descriptors.isInteropLibrary
import org.jetbrains.kotlin.backend.konan.ir.interop.IrProviderForCEnumAndCStructStubs
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.library.metadata.DeserializedKlibModuleOrigin
import org.jetbrains.kotlin.library.metadata.klibModuleOrigin
import org.jetbrains.kotlin.descriptors.konan.isNativeStdlib
import org.jetbrains.kotlin.fir.lazy.Fir2IrLazyClass
import org.jetbrains.kotlin.ir.IrBuiltIns
@@ -52,6 +50,8 @@ import org.jetbrains.kotlin.ir.visitors.IrElementTransformer
import org.jetbrains.kotlin.ir.visitors.IrElementVisitor
import org.jetbrains.kotlin.library.KotlinAbiVersion
import org.jetbrains.kotlin.library.KotlinLibrary
import org.jetbrains.kotlin.library.metadata.DeserializedKlibModuleOrigin
import org.jetbrains.kotlin.library.metadata.klibModuleOrigin
import org.jetbrains.kotlin.name.ClassId
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.name.Name
@@ -375,7 +375,7 @@ internal class KonanIrLinker(
mangler = KonanManglerIr,
typeSystem = IrTypeSystemContextImpl(builtIns),
friendModules = friendModules,
partialLinkageEnabled = partialLinkageSupport.partialLinkageEnabled,
partialLinkageEnabled = partialLinkageSupport.isEnabled,
platformSpecificClassFilter = KonanFakeOverrideClassFilter
)
@@ -698,6 +698,13 @@ internal class KonanIrLinker(
deserializedSymbols[idSig]?.let { return it }
val descriptor = descriptorByIdSignatureFinder.findDescriptorBySignature(idSig) ?: return null
if (partialLinkageSupport.isEnabled
&& descriptor.isTopLevelInPackage()
&& (descriptor as? DeclarationDescriptorWithVisibility)?.visibility == DescriptorVisibilities.PRIVATE
&& with(KonanManglerDesc) { !descriptor.isPlatformSpecificExport() }
) {
return null // Fixes case #1 in KT-54469
}
descriptorSignatures[descriptor] = idSig
@@ -794,12 +801,17 @@ internal class KonanIrLinker(
}
}
partialLinkageSupport.markUsedClassifiersExcludingUnlinkedFromFakeOverrideBuilding(fakeOverrideBuilder)
partialLinkageSupport.markUsedClassifiersInInlineLazyIrFunction(function)
partialLinkageSupport.exploreClassifiers(fakeOverrideBuilder)
partialLinkageSupport.exploreClassifiersInInlineLazyIrFunction(function)
fakeOverrideBuilder.provideFakeOverrides()
partialLinkageSupport.processUnlinkedDeclarations(linker.messageLogger) { listOf(function) }
partialLinkageSupport.generateStubsAndPatchUsages(symbolTable, function)
linker.checkNoUnboundSymbols(
symbolTable,
"after deserializing lazy-IR function ${function.name.asString()} in inline functions lowering"
)
return InlineFunctionOriginInfo(function, fileDeserializationState.file, inlineFunctionReference.startOffset, inlineFunctionReference.endOffset)
}
@@ -994,7 +1006,7 @@ internal class KonanIrLinker(
if (actualModule !== moduleDescriptor) {
val moduleDeserializer = resolveModuleDeserializer(actualModule, idSig)
moduleDeserializer.addModuleReachableTopLevel(idSig)
return symbolTable.referenceClass(idSig, false)
return symbolTable.referenceClass(idSig)
}
return declaredDeclaration.getOrPut(idSig) { buildForwardDeclarationStub(descriptor) }.symbol
@@ -5,8 +5,8 @@
package org.jetbrains.kotlin.backend.konan.serialization
import org.jetbrains.kotlin.backend.common.linkage.issues.UserVisibleIrModulesSupport
import org.jetbrains.kotlin.backend.common.serialization.IrModuleDeserializer
import org.jetbrains.kotlin.backend.common.serialization.linkerissues.UserVisibleIrModulesSupport
import org.jetbrains.kotlin.konan.file.File
import org.jetbrains.kotlin.konan.library.KONAN_PLATFORM_LIBS_NAME_PREFIX
import org.jetbrains.kotlin.konan.library.KONAN_STDLIB_NAME
@@ -16,8 +16,10 @@ import org.jetbrains.kotlin.backend.common.extensions.FirIncompatiblePluginAPI
import org.jetbrains.kotlin.backend.common.extensions.IrGenerationExtension
import org.jetbrains.kotlin.backend.common.extensions.IrPluginContext
import org.jetbrains.kotlin.backend.common.ir.addDispatchReceiver
import org.jetbrains.kotlin.backend.common.lower.ConstructorDelegationKind
import org.jetbrains.kotlin.backend.common.lower.DeclarationIrBuilder
import org.jetbrains.kotlin.backend.common.lower.callsSuper
import org.jetbrains.kotlin.backend.common.lower.delegationKind
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.ir.IrStatement
import org.jetbrains.kotlin.ir.ObsoleteDescriptorBasedAPI
@@ -204,7 +206,7 @@ private class AndroidIrTransformer(val extension: AndroidIrExtension, val plugin
declaration.declarations += declaration.getCachedFindViewByIdFun()
for (constructor in declaration.constructors) {
if (!constructor.callsSuper(pluginContext.irBuiltIns)) continue
if (constructor.delegationKind(pluginContext.irBuiltIns) != ConstructorDelegationKind.CALLS_SUPER) continue
// Initialize the cache as the first thing, even before the super constructor is called. This ensures
// that if the super constructor calls an override declared in this class, the cache already exists.
val body = constructor.body as? IrBlockBody ?: continue