diff --git a/compiler/cli/cli-js/src/org/jetbrains/kotlin/cli/js/klib/irForKlib.kt b/compiler/cli/cli-js/src/org/jetbrains/kotlin/cli/js/klib/irForKlib.kt index c5fadb42e4b..aec89457a79 100644 --- a/compiler/cli/cli-js/src/org/jetbrains/kotlin/cli/js/klib/irForKlib.kt +++ b/compiler/cli/cli-js/src/org/jetbrains/kotlin/cli/js/klib/irForKlib.kt @@ -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 diff --git a/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/CommonBackendContext.kt b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/CommonBackendContext.kt index aca14b7a53c..83434ec175b 100644 --- a/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/CommonBackendContext.kt +++ b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/CommonBackendContext.kt @@ -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 } /** diff --git a/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/ir/Ir.kt b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/ir/Ir.kt index b1017bcdffa..fcff6f2f54d 100644 --- a/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/ir/Ir.kt +++ b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/ir/Ir.kt @@ -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 diff --git a/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/LocalDeclarationsLowering.kt b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/LocalDeclarationsLowering.kt index 0d539cd8be5..338d420e179 100644 --- a/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/LocalDeclarationsLowering.kt +++ b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/LocalDeclarationsLowering.kt @@ -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> = 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) } diff --git a/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/LowerUtils.kt b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/LowerUtils.kt index e44af59681f..929c3356459 100644 --- a/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/LowerUtils.kt +++ b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/LowerUtils.kt @@ -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( diff --git a/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/coroutines/AddContinuationToFunctionCallsLowering.kt b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/coroutines/AddContinuationToFunctionCallsLowering.kt index 55bcd46d367..645a809201d 100644 --- a/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/coroutines/AddContinuationToFunctionCallsLowering.kt +++ b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/coroutines/AddContinuationToFunctionCallsLowering.kt @@ -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 + ) } diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/JsIrBackendContext.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/JsIrBackendContext.kt index a57792e9688..7c1ecb2b486 100644 --- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/JsIrBackendContext.kt +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/JsIrBackendContext.kt @@ -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 + ) } diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/compiler.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/compiler.kt index 0168b54470a..8e0f9e552e2 100644 --- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/compiler.kt +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/compiler.kt @@ -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 diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/ic/JsIrLinkerLoader.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/ic/JsIrLinkerLoader.kt index 852cbbe59e9..89107bcc5e7 100644 --- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/ic/JsIrLinkerLoader.kt +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/ic/JsIrLinkerLoader.kt @@ -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 diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/coroutines/AddContinuationToFunctionCallsLowering.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/coroutines/AddContinuationToFunctionCallsLowering.kt index 5a71c7608eb..f232e9c9edb 100644 --- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/coroutines/AddContinuationToFunctionCallsLowering.kt +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/coroutines/AddContinuationToFunctionCallsLowering.kt @@ -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 + } } \ No newline at end of file diff --git a/compiler/ir/backend.jvm/entrypoint/src/org/jetbrains/kotlin/backend/jvm/JvmIrCodegenFactory.kt b/compiler/ir/backend.jvm/entrypoint/src/org/jetbrains/kotlin/backend/jvm/JvmIrCodegenFactory.kt index d39765dbcd8..95a89a1fa21 100644 --- a/compiler/ir/backend.jvm/entrypoint/src/org/jetbrains/kotlin/backend/jvm/JvmIrCodegenFactory.kt +++ b/compiler/ir/backend.jvm/entrypoint/src/org/jetbrains/kotlin/backend/jvm/JvmIrCodegenFactory.kt @@ -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 diff --git a/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/compiler.kt b/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/compiler.kt index 5e63c1a5e62..a9080e2c6d6 100644 --- a/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/compiler.kt +++ b/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/compiler.kt @@ -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 diff --git a/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/expressions/IrStatementOrigin.kt b/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/expressions/IrStatementOrigin.kt index ac944ff8327..18c6efb8675 100644 --- a/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/expressions/IrStatementOrigin.kt +++ b/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/expressions/IrStatementOrigin.kt @@ -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) } diff --git a/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/linkage/partial/ExploredClassifier.kt b/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/linkage/partial/ExploredClassifier.kt new file mode 100644 index 00000000000..fc245c33b4a --- /dev/null +++ b/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/linkage/partial/ExploredClassifier.kt @@ -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) : 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 +} diff --git a/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/overrides/IrUnimplementedOverridesStrategy.kt b/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/linkage/partial/IrUnimplementedOverridesStrategy.kt similarity index 85% rename from compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/overrides/IrUnimplementedOverridesStrategy.kt rename to compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/linkage/partial/IrUnimplementedOverridesStrategy.kt index 8cb29b00c26..0f095e43193 100644 --- a/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/overrides/IrUnimplementedOverridesStrategy.kt +++ b/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/linkage/partial/IrUnimplementedOverridesStrategy.kt @@ -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) } } diff --git a/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/linkage/partial/PartialLinkageCase.kt b/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/linkage/partial/PartialLinkageCase.kt new file mode 100644 index 00000000000..230746f8ca8 --- /dev/null +++ b/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/linkage/partial/PartialLinkageCase.kt @@ -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, + 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) : 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 +} diff --git a/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/linkage/partial/PartialLinkageSupportForLowerings.kt b/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/linkage/partial/PartialLinkageSupportForLowerings.kt new file mode 100644 index 00000000000..48198e5957e --- /dev/null +++ b/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/linkage/partial/PartialLinkageSupportForLowerings.kt @@ -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") + } + } +} diff --git a/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/linkage/partial/PartialLinkageUtils.kt b/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/linkage/partial/PartialLinkageUtils.kt new file mode 100644 index 00000000000..071928e0138 --- /dev/null +++ b/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/linkage/partial/PartialLinkageUtils.kt @@ -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 = "" + } + + object MissingDeclarations : Module { + override val name = "" + } + + 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 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() + } + } + } +} diff --git a/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/linkage/partial/PartiallyLinkedDeclarationOrigin.kt b/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/linkage/partial/PartiallyLinkedDeclarationOrigin.kt new file mode 100644 index 00000000000..01f76a881b6 --- /dev/null +++ b/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/linkage/partial/PartiallyLinkedDeclarationOrigin.kt @@ -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; +} diff --git a/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/overrides/CopyIrTreeWithSymbolsForFakeOverrides.kt b/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/overrides/CopyIrTreeWithSymbolsForFakeOverrides.kt index e8f941483a4..216cea8e6df 100644 --- a/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/overrides/CopyIrTreeWithSymbolsForFakeOverrides.kt +++ b/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/overrides/CopyIrTreeWithSymbolsForFakeOverrides.kt @@ -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.* diff --git a/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/overrides/FakeOverrideCopier.kt b/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/overrides/FakeOverrideCopier.kt index 213762a4f29..dce5fe21648 100644 --- a/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/overrides/FakeOverrideCopier.kt +++ b/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/overrides/FakeOverrideCopier.kt @@ -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) - } } } } diff --git a/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/overrides/IrOverridingUtil.kt b/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/overrides/IrOverridingUtil.kt index a4c87c3dcc1..d30bbff1bf7 100644 --- a/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/overrides/IrOverridingUtil.kt +++ b/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/overrides/IrOverridingUtil.kt @@ -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() @@ -237,30 +238,22 @@ class IrOverridingUtil( ): Collection { val bound = ArrayList(descriptorsFromSuper.size) val overridden = mutableSetOf() + 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, - current: IrClass, + currentClass: IrClass, addedFakeOverrides: MutableList, 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 + val subTypeParameters: List - val superValueParameters = superMember.compiledValueParameters - val subValueParameters = subMember.compiledValueParameters - val superTypeParameters = superMember.typeParameters - val subTypeParameters = subMember.typeParameters + val superValueParameters: List + val subValueParameters: List - 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 + get() = ArrayList(valueParameters.size + 1).apply { + extensionReceiverParameter?.let(::add) + addAll(valueParameters) + } + +private val IrProperty.compiledValueParameters: List + get() = getter?.extensionReceiverParameter?.let(::listOf).orEmpty() + +private val IrProperty.typeParameters: List + get() = getter?.typeParameters.orEmpty() + +private val IrProperty.isInline: Boolean + get() = getter?.isInline == true || setter?.isInline == true + +private val IrOverridableMember.typeParameters: List + 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() diff --git a/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/overrides/IrVisibilityUtil.kt b/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/overrides/IrVisibilityUtil.kt index d604f17b964..40d85739822 100644 --- a/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/overrides/IrVisibilityUtil.kt +++ b/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/overrides/IrVisibilityUtil.kt @@ -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): 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 + } +} diff --git a/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/util/ConstantValueGenerator.kt b/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/util/ConstantValueGenerator.kt index d13d551a903..f2583a7b61f 100644 --- a/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/util/ConstantValueGenerator.kt +++ b/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/util/ConstantValueGenerator.kt @@ -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 diff --git a/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/util/IrUtils.kt b/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/util/IrUtils.kt index 29a4e2dc3f0..e655291eec0 100644 --- a/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/util/IrUtils.kt +++ b/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/util/IrUtils.kt @@ -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 { - val result = mutableListOf() +fun > T.allOverridden(includeSelf: Boolean = false): List { + val result = mutableListOf() if (includeSelf) { result.add(this) } @@ -1341,7 +1341,8 @@ fun IrSimpleFunction.allOverridden(includeSelf: Boolean = false): List 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) { - for (overriddenSymbol in function.overriddenSymbols) { - val override = overriddenSymbol.owner +private fun > computeAllOverridden(overridable: T, result: MutableSet) { + for (overriddenSymbol in overridable.overriddenSymbols) { + @Suppress("UNCHECKED_CAST") val override = overriddenSymbol.owner as T if (result.add(override)) { computeAllOverridden(override, result) } diff --git a/compiler/ir/serialization.common/src/org/jetbrains/kotlin/backend/common/serialization/linkerissues/IrDeserializationExceptions.kt b/compiler/ir/serialization.common/src/org/jetbrains/kotlin/backend/common/linkage/issues/IrDeserializationExceptions.kt similarity index 93% rename from compiler/ir/serialization.common/src/org/jetbrains/kotlin/backend/common/serialization/linkerissues/IrDeserializationExceptions.kt rename to compiler/ir/serialization.common/src/org/jetbrains/kotlin/backend/common/linkage/issues/IrDeserializationExceptions.kt index f979f14c008..e360970800c 100644 --- a/compiler/ir/serialization.common/src/org/jetbrains/kotlin/backend/common/serialization/linkerissues/IrDeserializationExceptions.kt +++ b/compiler/ir/serialization.common/src/org/jetbrains/kotlin/backend/common/linkage/issues/IrDeserializationExceptions.kt @@ -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 diff --git a/compiler/ir/serialization.common/src/org/jetbrains/kotlin/backend/common/serialization/linkerissues/KotlinIrLinkerIssues.kt b/compiler/ir/serialization.common/src/org/jetbrains/kotlin/backend/common/linkage/issues/KotlinIrLinkerIssues.kt similarity index 98% rename from compiler/ir/serialization.common/src/org/jetbrains/kotlin/backend/common/serialization/linkerissues/KotlinIrLinkerIssues.kt rename to compiler/ir/serialization.common/src/org/jetbrains/kotlin/backend/common/linkage/issues/KotlinIrLinkerIssues.kt index f2ef9a452e8..a61429a7299 100644 --- a/compiler/ir/serialization.common/src/org/jetbrains/kotlin/backend/common/serialization/linkerissues/KotlinIrLinkerIssues.kt +++ b/compiler/ir/serialization.common/src/org/jetbrains/kotlin/backend/common/linkage/issues/KotlinIrLinkerIssues.kt @@ -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 diff --git a/compiler/ir/serialization.common/src/org/jetbrains/kotlin/backend/common/serialization/linkerissues/UserVisibleIrModulesSupport.kt b/compiler/ir/serialization.common/src/org/jetbrains/kotlin/backend/common/linkage/issues/UserVisibleIrModulesSupport.kt similarity index 99% rename from compiler/ir/serialization.common/src/org/jetbrains/kotlin/backend/common/serialization/linkerissues/UserVisibleIrModulesSupport.kt rename to compiler/ir/serialization.common/src/org/jetbrains/kotlin/backend/common/linkage/issues/UserVisibleIrModulesSupport.kt index 6631761c9b0..61ebad754bb 100644 --- a/compiler/ir/serialization.common/src/org/jetbrains/kotlin/backend/common/serialization/linkerissues/UserVisibleIrModulesSupport.kt +++ b/compiler/ir/serialization.common/src/org/jetbrains/kotlin/backend/common/linkage/issues/UserVisibleIrModulesSupport.kt @@ -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 diff --git a/compiler/ir/serialization.common/src/org/jetbrains/kotlin/backend/common/serialization/linkerissues/checks.kt b/compiler/ir/serialization.common/src/org/jetbrains/kotlin/backend/common/linkage/issues/checks.kt similarity index 60% rename from compiler/ir/serialization.common/src/org/jetbrains/kotlin/backend/common/serialization/linkerissues/checks.kt rename to compiler/ir/serialization.common/src/org/jetbrains/kotlin/backend/common/linkage/issues/checks.kt index e30c0a9b043..afe2fec85fc 100644 --- a/compiler/ir/serialization.common/src/org/jetbrains/kotlin/backend/common/serialization/linkerissues/checks.kt +++ b/compiler/ir/serialization.common/src/org/jetbrains/kotlin/backend/common/linkage/issues/checks.kt @@ -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 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 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) { diff --git a/compiler/ir/serialization.common/src/org/jetbrains/kotlin/backend/common/linkage/partial/ClassifierExplorer.kt b/compiler/ir/serialization.common/src/org/jetbrains/kotlin/backend/common/linkage/partial/ClassifierExplorer.kt new file mode 100644 index 00000000000..de1e5419860 --- /dev/null +++ b/compiler/ir/serialization.common/src/org/jetbrains/kotlin/backend/common/linkage/partial/ClassifierExplorer.kt @@ -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 by lazy { + setOf( + builtIns.stringClass, // kotlin.String + builtIns.kClassClass // kotlin.reflect.KClass + ) + } + + private val permittedAnnotationParameterSymbols: Set by lazy { + buildSet { + this += permittedAnnotationArrayParameterSymbols + + PrimitiveType.values().forEach { + addIfNotNull(builtIns.findClass(it.typeName, BUILT_INS_PACKAGE_FQ_NAME)) // kotlin. + addIfNotNull(builtIns.findClass(it.arrayTypeName, BUILT_INS_PACKAGE_FQ_NAME)) // kotlin.Array + } + + UnsignedType.values().forEach { + addIfNotNull(builtIns.findClass(it.typeName, BUILT_INS_PACKAGE_FQ_NAME)) // kotlin.U + addIfNotNull(builtIns.findClass(it.arrayClassId.shortClassName, BUILT_INS_PACKAGE_FQ_NAME)) // kotlin.UArray + } + } + } + + 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): 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): 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() + + 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): 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, + 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): 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? + 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 Iterable.firstUnusable(transform: (T) -> ExploredClassifier?): Unusable? = + firstNotNullOfOrNull { transform(it).asUnusable() } + + private inline fun Sequence.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) + } +} diff --git a/compiler/ir/serialization.common/src/org/jetbrains/kotlin/backend/common/linkage/partial/ExploredClassifiers.kt b/compiler/ir/serialization.common/src/org/jetbrains/kotlin/backend/common/linkage/partial/ExploredClassifiers.kt new file mode 100644 index 00000000000..1549d36f6dd --- /dev/null +++ b/compiler/ir/serialization.common/src/org/jetbrains/kotlin/backend/common/linkage/partial/ExploredClassifiers.kt @@ -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() + private val unusableSymbols = HashMap() + + 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 + } +} diff --git a/compiler/ir/serialization.common/src/org/jetbrains/kotlin/backend/common/linkage/partial/IrUnimplementedOverridesStrategy.kt b/compiler/ir/serialization.common/src/org/jetbrains/kotlin/backend/common/linkage/partial/IrUnimplementedOverridesStrategy.kt new file mode 100644 index 00000000000..47e9f127518 --- /dev/null +++ b/compiler/ir/serialization.common/src/org/jetbrains/kotlin/backend/common/linkage/partial/IrUnimplementedOverridesStrategy.kt @@ -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 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 +} diff --git a/compiler/ir/serialization.common/src/org/jetbrains/kotlin/backend/common/linkage/partial/MissingDeclarationStubGenerator.kt b/compiler/ir/serialization.common/src/org/jetbrains/kotlin/backend/common/linkage/partial/MissingDeclarationStubGenerator.kt new file mode 100644 index 00000000000..99d9bd45bef --- /dev/null +++ b/compiler/ir/serialization.common/src/org/jetbrains/kotlin/backend/common/linkage/partial/MissingDeclarationStubGenerator.kt @@ -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() + + fun grabDeclarationsToPatch(): Collection { + 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.setCommonParent(): T { + parent = commonParent + declarationsToPatch += this + return this + } + + private fun IrSymbol.guessName(): Name = + signature?.guessName(nameSegmentsToPickUp = 1)?.let(Name::guessByFirstCharacter) ?: PartialLinkageUtils.UNKNOWN_NAME +} diff --git a/compiler/ir/serialization.common/src/org/jetbrains/kotlin/backend/common/linkage/partial/PartialLinkageErrorMessages.kt b/compiler/ir/serialization.common/src/org/jetbrains/kotlin/backend/common/linkage/partial/PartialLinkageErrorMessages.kt new file mode 100644 index 00000000000..6e41dddb1cc --- /dev/null +++ b/compiler/ir/serialization.common/src/org/jetbrains/kotlin/backend/common/linkage/partial/PartialLinkageErrorMessages.kt @@ -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. or class_name.Companion. + 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 = "" +private const val 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 { + // + object Standalone : CauseRendering + + // uses . + // or + // uses via . + sealed interface UsedFromSomewhere : CauseRendering { + val objectText: String + val objectSymbol: IrSymbol? + } + + // := + class UsedFromDeclaration(override val objectText: String, override val objectSymbol: IrSymbol) : UsedFromSomewhere + + // := + 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(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): 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) +} diff --git a/compiler/ir/serialization.common/src/org/jetbrains/kotlin/backend/common/linkage/partial/PartialLinkageSupportForLinker.kt b/compiler/ir/serialization.common/src/org/jetbrains/kotlin/backend/common/linkage/partial/PartialLinkageSupportForLinker.kt new file mode 100644 index 00000000000..2bc7a507994 --- /dev/null +++ b/compiler/ir/serialization.common/src/org/jetbrains/kotlin/backend/common/linkage/partial/PartialLinkageSupportForLinker.kt @@ -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 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) + 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) = Unit + override fun generateStubsAndPatchUsages(symbolTable: SymbolTable, root: IrDeclaration) = Unit + } + } +} diff --git a/compiler/ir/serialization.common/src/org/jetbrains/kotlin/backend/common/linkage/partial/PartialLinkageSupportForLinkerImpl.kt b/compiler/ir/serialization.common/src/org/jetbrains/kotlin/backend/common/linkage/partial/PartialLinkageSupportForLinkerImpl.kt new file mode 100644 index 00000000000..107b7b28486 --- /dev/null +++ b/compiler/ir/serialization.common/src/org/jetbrains/kotlin/backend/common/linkage/partial/PartialLinkageSupportForLinkerImpl.kt @@ -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) { + 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()) + } +} diff --git a/compiler/ir/serialization.common/src/org/jetbrains/kotlin/backend/common/linkage/partial/PartialLinkageSupportForLoweringsImpl.kt b/compiler/ir/serialization.common/src/org/jetbrains/kotlin/backend/common/linkage/partial/PartialLinkageSupportForLoweringsImpl.kt new file mode 100644 index 00000000000..602a0a6344b --- /dev/null +++ b/compiler/ir/serialization.common/src/org/jetbrains/kotlin/backend/common/linkage/partial/PartialLinkageSupportForLoweringsImpl.kt @@ -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 + } + } + } +} diff --git a/compiler/ir/serialization.common/src/org/jetbrains/kotlin/backend/common/linkage/partial/PartialLinkageUtils.kt b/compiler/ir/serialization.common/src/org/jetbrains/kotlin/backend/common/linkage/partial/PartialLinkageUtils.kt new file mode 100644 index 00000000000..f72aa7445cf --- /dev/null +++ b/compiler/ir/serialization.common/src/org/jetbrains/kotlin/backend/common/linkage/partial/PartialLinkageUtils.kt @@ -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("") + + 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 + } + } +} diff --git a/compiler/ir/serialization.common/src/org/jetbrains/kotlin/backend/common/linkage/partial/PartiallyLinkedIrTreePatcher.kt b/compiler/ir/serialization.common/src/org/jetbrains/kotlin/backend/common/linkage/partial/PartiallyLinkedIrTreePatcher.kt new file mode 100644 index 00000000000..559817f0bd5 --- /dev/null +++ b/compiler/ir/serialization.common/src/org/jetbrains/kotlin/backend/common/linkage/partial/PartiallyLinkedIrTreePatcher.kt @@ -0,0 +1,1003 @@ +/* + * 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.DeclarationId +import org.jetbrains.kotlin.backend.common.linkage.partial.PartialLinkageUtils.DeclarationId.Companion.declarationId +import org.jetbrains.kotlin.backend.common.linkage.partial.PartialLinkageUtils.isEffectivelyMissingLazyIrDeclaration +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.IrElement +import org.jetbrains.kotlin.ir.IrStatement +import org.jetbrains.kotlin.ir.declarations.* +import org.jetbrains.kotlin.ir.declarations.lazy.IrLazyDeclarationBase +import org.jetbrains.kotlin.ir.expressions.* +import org.jetbrains.kotlin.ir.expressions.IrStatementOrigin.PARTIAL_LINKAGE_RUNTIME_ERROR +import org.jetbrains.kotlin.ir.expressions.impl.IrCompositeImpl +import org.jetbrains.kotlin.ir.linkage.partial.ExploredClassifier +import org.jetbrains.kotlin.ir.linkage.partial.PartialLinkageCase +import org.jetbrains.kotlin.ir.linkage.partial.PartialLinkageCase.* +import org.jetbrains.kotlin.ir.linkage.partial.PartiallyLinkedDeclarationOrigin +import org.jetbrains.kotlin.ir.linkage.partial.isPartialLinkageRuntimeError +import org.jetbrains.kotlin.ir.overrides.isEffectivelyPrivate +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.util.* +import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid +import org.jetbrains.kotlin.ir.visitors.IrElementVisitorVoid +import org.jetbrains.kotlin.ir.visitors.acceptChildrenVoid +import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid +import org.jetbrains.kotlin.utils.addIfNotNull +import org.jetbrains.kotlin.utils.compact +import java.util.* +import kotlin.properties.Delegates +import org.jetbrains.kotlin.ir.linkage.partial.PartialLinkageUtils.File as PLFile +import org.jetbrains.kotlin.ir.linkage.partial.PartialLinkageUtils.Module as PLModule + +internal class PartiallyLinkedIrTreePatcher( + private val builtIns: IrBuiltIns, + private val classifierExplorer: ClassifierExplorer, + private val stubGenerator: MissingDeclarationStubGenerator, + private val messageLogger: IrMessageLogger +) { + // Avoid revisiting roots that already have been visited. + private val visitedModuleFragments = hashSetOf() + private val visitedDeclarations = hashSetOf() + + private val stdlibModule by lazy { PLModule.determineModuleFor(builtIns.anyClass.owner) } + + private val PLModule.shouldBeSkipped: Boolean get() = this == PLModule.SyntheticBuiltInFunctions || this == stdlibModule + private val IrModuleFragment.shouldBeSkipped: Boolean get() = files.isEmpty() || name.asString() == stdlibModule.name + + // Used only to generate IR expressions that throw linkage errors. + private val supportForLowerings by lazy { PartialLinkageSupportForLoweringsImpl(builtIns, messageLogger) } + + fun patchModuleFragments(roots: Sequence) { + roots.forEach { root -> + // Optimization: Don't patch stdlib and already visited fragments. + if (!root.shouldBeSkipped && visitedModuleFragments.add(root)) { + root.transformVoid(DeclarationTransformer(startingFile = null)) + root.transformVoid(ExpressionTransformer(startingFile = null)) + root.transformVoid(NonLocalReturnsPatcher(startingFile = null)) + } + } + } + + fun patchDeclarations(roots: Collection) { + roots.forEach { root -> + val startingFile = PLFile.determineFileFor(root) + // Optimization: Don't patch already visited declarations and declarations from stdlib/built-ins. + if (!startingFile.module.shouldBeSkipped && visitedDeclarations.add(root)) { + root.transformVoid(DeclarationTransformer(startingFile)) + root.transformVoid(ExpressionTransformer(startingFile)) + root.transformVoid(NonLocalReturnsPatcher(startingFile)) + } + } + } + + private fun IrElement.transformVoid(transformer: IrElementTransformerVoid) { + transform(transformer, null) + } + + private sealed class DeclarationTransformerContext { + private val scheduledForRemoval = HashSet() + + fun scheduleForRemoval(declaration: IrDeclaration) { + scheduledForRemoval += declaration + } + + abstract fun performRemoval() + + protected fun performRemoval(declarations: MutableList, container: IrElement) { + val expectedToRemove = scheduledForRemoval.size + if (expectedToRemove == 0) return + + var removed = declarations.size + declarations.removeAll(scheduledForRemoval) + removed -= declarations.size + + assert(expectedToRemove == removed) { + "Expected to remove $expectedToRemove declarations in $container, but removed only $removed" + } + } + + class DeclarationContainer(val declarationContainer: IrDeclarationContainer) : DeclarationTransformerContext() { + override fun performRemoval() = performRemoval(declarationContainer.declarations, declarationContainer) + } + + class StatementContainer(val statementContainer: IrStatementContainer) : DeclarationTransformerContext() { + override fun performRemoval() = performRemoval(statementContainer.statements, statementContainer) + } + } + + // Declarations are transformed top-down. + private inner class DeclarationTransformer(startingFile: PLFile?) : FileAwareIrElementTransformerVoid(startingFile) { + private val stack = ArrayDeque() + + private fun T.transformChildren(): T { + transformChildrenVoid() + return this + } + + private fun T.transformChildrenWithRemoval(): T = + transformChildrenWithRemoval(DeclarationTransformerContext.DeclarationContainer(this)) + + private fun T.transformChildrenWithRemoval(): T = + transformChildrenWithRemoval(DeclarationTransformerContext.StatementContainer(this)) + + private fun T.transformChildrenWithRemoval(context: DeclarationTransformerContext): T { + stack.push(context) + transformChildrenVoid() + assert(stack.pop() === context) + + context.performRemoval() + + return this + } + + private fun IrDeclaration.scheduleForRemoval() { + // The declarations with origin = PartiallyLinkedDeclarationOrigin.MISSING_DECLARATION are already effectively removed. + if (origin != PartiallyLinkedDeclarationOrigin.MISSING_DECLARATION) + stack.peek().scheduleForRemoval(this) + } + + override fun visitPackageFragment(declaration: IrPackageFragment): IrPackageFragment { + return declaration.transformChildrenWithRemoval() + } + + override fun visitClass(declaration: IrClass): IrStatement { + // Discover the reason why the class itself is unusable. + val unusableClass = declaration.symbol.explore() + if (unusableClass != null) { + // Transform the reason into the most appropriate linkage case. + val partialLinkageCase = when (unusableClass) { + is ExploredClassifier.Unusable.CanBeRootCause -> UnusableClassifier(unusableClass) + is ExploredClassifier.Unusable.DueToOtherClassifier -> DeclarationWithUnusableClassifier( + declaration.symbol, + unusableClass.rootCause + ) + } + + // Get anonymous initializer. + val anonInitializer = declaration.declarations.firstNotNullOfOrNull { it as? IrAnonymousInitializer } + ?: builtIns.irFactory.createAnonymousInitializer( + declaration.startOffset, + declaration.endOffset, + PartiallyLinkedDeclarationOrigin.AUXILIARY_GENERATED_DECLARATION, + IrAnonymousInitializerSymbolImpl() + ).apply { + body = builtIns.irFactory.createBlockBody(declaration.startOffset, declaration.endOffset) + parent = declaration + declaration.declarations += this + } + + // Clean initializer body. Don't process underlying statements. + anonInitializer.body.statements.clear() + + // Generate IR call that throws linkage error. Report compiler warning. + anonInitializer.body.statements += partialLinkageCase.throwLinkageError( + anonInitializer, + suppressWarningInCompilerOutput = false + ) + + // Finish processing of the current class. + declaration.typeParameters.forEach { tp -> + tp.superTypes.toPartiallyLinkedMarkerTypeOrNull()?.let { newSuperType -> + tp.superTypes = listOf(newSuperType) + } + } + + declaration.superTypes = declaration.superTypes.filter { /* filter unusable */ it.explore() == null } + + /** + * Remove the class in the following cases: + * - It is a local class (or anonymous object) + * - It is an inner class + * - It is a class without non-inner underlying classes + * + * The removal of local/inner class leads to removal of all underlying declarations including any classes declared + * under the removed class. In all cases that could be only inner classes that share state with their containing + * class and that become unusable together with the containing class. + * + * The removal of class of any other type is not performed: Such class may have nested classes that do not share + * their state with the containing class and not necessarily become unusable together with the containing class. + */ + if (declaration.isLocal || declaration.isInner || declaration.declarations.none { (it as? IrClass)?.isInner == false }) { + declaration.scheduleForRemoval() // Don't process underlying declarations. + return declaration + } + } + + // Process underlying declarations. Collect declarations to remove. + return declaration.transformChildrenWithRemoval() + } + + override fun visitConstructor(declaration: IrConstructor): IrStatement { + // IMPORTANT: It's necessary to overwrite types. Please don't move the statement below. + val unusableClassifierInSignature = declaration.rewriteTypesInFunction() + + val invalidConstructorDelegation = declaration.checkConstructorDelegation() + + // Compute the linkage case. + val partialLinkageCase = if (declaration.origin == PartiallyLinkedDeclarationOrigin.MISSING_DECLARATION) + MissingDeclaration(declaration.symbol) + else + unusableClassifierInSignature?.let { DeclarationWithUnusableClassifier(declaration.symbol, it) } + ?: invalidConstructorDelegation + + if (partialLinkageCase != null) { + // Note: Block body is missing for MISSING_DECLARATION. + val blockBody = declaration.body as? IrBlockBody + ?: builtIns.irFactory.createBlockBody(declaration.startOffset, declaration.endOffset).apply { declaration.body = this } + + if (invalidConstructorDelegation != null) { + // Drop invalid delegating constructor call. Otherwise it may break some lowerings. + blockBody.statements.removeIf { it is IrDelegatingConstructorCall } + } + + // IMPORTANT: Unlike it's done for IrSimpleFunction don't clean-up statements. Insert PL linkage as the first one. + // This is necessary to preserve anonymous initializer call and delegating constructor call in place. + blockBody.statements.add( + 0, partialLinkageCase.throwLinkageError( + declaration, + // Note: Don't log errors for members of unlinked class. + // - All such members are unusable anyway since their dispatch receiver (class) is unusable. + // - Also, this reduces the number of compiler error messages and makes the compiler output less polluted. + suppressWarningInCompilerOutput = declaration.isDirectMemberOf(unusableClassifierInSignature) + ) + ) + } + + return declaration.transformChildren() + } + + override fun visitSimpleFunction(declaration: IrSimpleFunction): IrStatement { + declaration.filterOverriddenSymbols() + + // IMPORTANT: It's necessary to overwrite types. Please don't move the statement below. + val unusableClassifierInSignature = declaration.rewriteTypesInFunction() + + // Compute the linkage case. + val partialLinkageCase = when (declaration.origin) { + PartiallyLinkedDeclarationOrigin.UNIMPLEMENTED_ABSTRACT_CALLABLE_MEMBER -> UnimplementedAbstractCallable(declaration) + PartiallyLinkedDeclarationOrigin.MISSING_DECLARATION -> MissingDeclaration(declaration.symbol) + else -> unusableClassifierInSignature?.let { DeclarationWithUnusableClassifier(declaration.symbol, it) } + } + + if (partialLinkageCase != null) { + // Note: Block body is missing for UNIMPLEMENTED_ABSTRACT_CALLABLE_MEMBER and MISSING_DECLARATION. + val blockBody = declaration.body as? IrBlockBody + ?: builtIns.irFactory.createBlockBody(declaration.startOffset, declaration.endOffset).apply { declaration.body = this } + + // Clean initializer body. Don't process underlying statements. + blockBody.statements.clear() + + // Generate IR call that throws linkage error. Report compiler warning. + blockBody.statements += partialLinkageCase.throwLinkageError( + declaration, + // Note: Don't log errors for members of unlinked class. + // - All such members are unusable anyway since their dispatch receiver (class) is unusable. + // - Also, this reduces the number of compiler error messages and makes the compiler output less polluted. + suppressWarningInCompilerOutput = declaration.isDirectMemberOf(unusableClassifierInSignature) + ) + + // Don't remove inline functions, this may harm linkage in K/N backend with enabled static caches. + if (!declaration.isInline) { + if (declaration.isTopLevelDeclaration) { + // Optimization: Remove unlinked top-level functions. + declaration.scheduleForRemoval() + } else { + // Optimization: Remove unlinked top-level properties. + val property = declaration.correspondingPropertySymbol?.owner + if (property?.isTopLevelDeclaration == true) + property.scheduleForRemoval() + } + } + + // Return the function. There is nothing to process below it. + return declaration + } + + // Process underlying declarations. Collect declarations to remove. + return declaration.transformChildren() + } + + /** + * Checks if there is an issue with constructor delegation. + */ + private fun IrConstructor.checkConstructorDelegation(): InvalidConstructorDelegation? { + if (origin == PartiallyLinkedDeclarationOrigin.MISSING_DECLARATION) + return null + + val statements = (body as? IrBlockBody)?.statements ?: return null + + val constructedClass = parentAsClass + val constructedClassSymbol = constructedClass.symbol + + val actualSuperClassSymbol = constructedClass.superTypes.firstNotNullOfOrNull { superType -> + val superClassSymbol = (superType as? IrSimpleType)?.classifier as? IrClassSymbol ?: return@firstNotNullOfOrNull null + if (superClassSymbol.owner.isClass) superClassSymbol else null + } ?: builtIns.anyClass + val actualSuperClass = actualSuperClassSymbol.owner + + statements.forEach { statement -> + if (statement !is IrDelegatingConstructorCall) return@forEach + + val calledConstructorSymbol = statement.symbol + val calledConstructor = calledConstructorSymbol.owner + + val invalidConstructorDelegationFound = + if (calledConstructor.origin != PartiallyLinkedDeclarationOrigin.MISSING_DECLARATION) { + val constructedSuperClassSymbol = calledConstructor.parentAsClass.symbol + constructedSuperClassSymbol != constructedClassSymbol && constructedSuperClassSymbol != actualSuperClassSymbol + } else { + // Fallback to signatures. + (calledConstructorSymbol.signature as? IdSignature.CommonSignature)?.let { constructorSignature -> + val constructedSuperClassId = DeclarationId( + constructorSignature.packageFqName, + constructorSignature.declarationFqName.substringBeforeLast('.') + ) + + actualSuperClass.declarationId != constructedSuperClassId + } ?: false + } + + if (invalidConstructorDelegationFound) + return InvalidConstructorDelegation( + constructorSymbol = symbol, + superClassSymbol = actualSuperClassSymbol, + unexpectedSuperClassConstructorSymbol = calledConstructorSymbol + ) + } + + return null + } + + /** + * Returns the first encountered [ExploredClassifier.Unusable]. + */ + private fun IrFunction.rewriteTypesInFunction(): ExploredClassifier.Unusable? { + // Remember the first assignment. Ignore all subsequent. + var result: ExploredClassifier.Unusable? by Delegates.vetoable(null) { _, oldValue, _ -> oldValue == null } + + fun IrValueParameter.fixType() { + val newType = type.toPartiallyLinkedMarkerTypeOrNull() ?: return + type = newType + if (varargElementType != null) varargElementType = newType + defaultValue = null + + result = newType.unusableClassifier + } + + dispatchReceiverParameter?.fixType() // The dispatcher (aka this) is intentionally the first one. + extensionReceiverParameter?.fixType() + valueParameters.forEach { it.fixType() } + + returnType.toPartiallyLinkedMarkerTypeOrNull()?.let { newReturnType -> + returnType = newReturnType + result = newReturnType.unusableClassifier + } + + typeParameters.forEach { tp -> + tp.superTypes.toPartiallyLinkedMarkerTypeOrNull()?.let { newSuperType -> + tp.superTypes = listOf(newSuperType) + result = newSuperType.unusableClassifier + } + } + + return result + } + + override fun visitProperty(declaration: IrProperty): IrStatement { + declaration.filterOverriddenSymbols() + return declaration.transformChildren() + } + + override fun visitField(declaration: IrField): IrStatement { + return declaration.type.toPartiallyLinkedMarkerTypeOrNull()?.let { newType -> + val property = declaration.correspondingPropertySymbol?.owner + if (property?.isTopLevelDeclaration == true) { + // Optimization: Remove unlinked top-level properties. + property.scheduleForRemoval() + } + + declaration.type = newType + declaration.initializer = null + declaration + } ?: declaration.transformChildren() + } + + override fun visitVariable(declaration: IrVariable): IrStatement { + return declaration.type.toPartiallyLinkedMarkerTypeOrNull()?.let { newType -> + declaration.type = newType + declaration.initializer = null + declaration + } ?: declaration.transformChildren() + } + + override fun visitBlockBody(body: IrBlockBody): IrBody { + return body.transformChildrenWithRemoval() + } + + override fun visitContainerExpression(expression: IrContainerExpression): IrExpression { + return expression.transformChildrenWithRemoval() + } + + private fun IrOverridableDeclaration.filterOverriddenSymbols() { + if (overriddenSymbols.isNotEmpty()) { + overriddenSymbols = overriddenSymbols.filterTo(ArrayList(overriddenSymbols.size)) { symbol -> + val owner = symbol.owner as IrDeclaration + owner.origin != PartiallyLinkedDeclarationOrigin.MISSING_DECLARATION + // Handle the case when the overridden declaration became private. + && (owner as? IrDeclarationWithVisibility)?.visibility != DescriptorVisibilities.PRIVATE + }.compact() + } + } + + private fun PartialLinkageCase.throwLinkageError(declaration: IrDeclaration, suppressWarningInCompilerOutput: Boolean): IrCall = + supportForLowerings.throwLinkageError(this, declaration, currentFile, suppressWarningInCompilerOutput) + } + + private inner class ExpressionTransformer(startingFile: PLFile?) : FileAwareIrElementTransformerVoid(startingFile) { + override fun visitPackageFragment(declaration: IrPackageFragment): IrPackageFragment { + (declaration as? IrFile)?.filterUnusableAnnotations() + return super.visitPackageFragment(declaration) + } + + override fun visitDeclaration(declaration: IrDeclarationBase): IrStatement { + // Optimization: Don't patch expressions under generated synthetic declarations. + return if (declaration.origin is PartiallyLinkedDeclarationOrigin) + declaration // There are neither expressions nor annotations to patch. + else { + declaration.filterUnusableAnnotations() + super.visitDeclaration(declaration) + } + } + + override fun visitBlockBody(body: IrBlockBody): IrBody { + return super.visitBlockBody(body).apply { + (this as? IrStatementContainer)?.statements?.eliminateDeadCodeStatements() + } + } + + override fun visitReturn(expression: IrReturn) = expression.maybeThrowLinkageError { + checkReferencedDeclaration(returnTargetSymbol, checkVisibility = false) + } + + override fun visitBlock(expression: IrBlock) = expression.maybeThrowLinkageError { + if (this is IrReturnableBlock) + checkReferencedDeclaration(symbol, checkVisibility = false) + else null + } + + override fun visitTypeOperator(expression: IrTypeOperatorCall) = expression.maybeThrowLinkageError { + checkExpressionType(typeOperand) + } + + override fun visitDeclarationReference(expression: IrDeclarationReference) = expression.maybeThrowLinkageError { + checkReferencedDeclaration(symbol) + } + + override fun visitClassReference(expression: IrClassReference) = expression.maybeThrowLinkageError { + checkReferencedDeclaration(symbol) ?: checkExpressionType(classType) + } + + override fun visitGetObjectValue(expression: IrGetObjectValue) = expression.maybeThrowLinkageError { + checkReferencedDeclaration(symbol) + ?: checkReferencedDeclarationType(symbol.owner, "object") { it.kind == ClassKind.OBJECT } + } + + override fun visitMemberAccess(expression: IrMemberAccessExpression<*>) = expression.maybeThrowLinkageError { + checkReferencedDeclaration(symbol) ?: checkExpressionTypeArguments() + } + + override fun visitCall(expression: IrCall) = expression.maybeThrowLinkageError { + checkReferencedDeclaration(symbol) + ?: checkReferencedDeclaration(superQualifierSymbol) + ?: checkExpressionTypeArguments() + ?: checkArgumentsAndValueParameters(symbol.owner) + } + + override fun visitConstructorCall(expression: IrConstructorCall) = expression.maybeThrowLinkageError { + checkReferencedDeclaration(symbol) + ?: checkNotAbstractClass() + ?: checkExpressionTypeArguments() + ?: checkReferencedDeclarationType(expression.symbol.owner.parentAsClass, "regular class") { constructedClass -> + constructedClass.kind == ClassKind.CLASS || constructedClass.kind == ClassKind.ANNOTATION_CLASS + } + ?: checkArgumentsAndValueParameters(symbol.owner) + } + + override fun visitEnumConstructorCall(expression: IrEnumConstructorCall) = expression.maybeThrowLinkageError { + checkReferencedDeclaration(symbol) + ?: checkExpressionTypeArguments() + ?: checkReferencedDeclarationType(expression.symbol.owner.parentAsClass, "enum class") { constructedClass -> + constructedClass.kind == ClassKind.ENUM_CLASS || constructedClass.symbol == builtIns.enumClass + } + ?: checkArgumentsAndValueParameters(symbol.owner) + } + + override fun visitDelegatingConstructorCall(expression: IrDelegatingConstructorCall) = expression.maybeThrowLinkageError { + checkReferencedDeclaration(symbol) + ?: checkExpressionTypeArguments() + ?: checkArgumentsAndValueParameters(symbol.owner) + } + + override fun visitFunctionReference(expression: IrFunctionReference) = expression.maybeThrowLinkageError { + checkReferencedDeclaration(symbol) + ?: checkReferencedDeclaration(reflectionTarget) + ?: checkExpressionTypeArguments() + ?: checkArgumentsAndValueParameters(symbol.owner) + } + + override fun visitPropertyReference(expression: IrPropertyReference) = expression.maybeThrowLinkageError { + checkReferencedDeclaration(symbol) + ?: checkReferencedDeclaration(getter) + ?: checkReferencedDeclaration(setter) + ?: checkReferencedDeclaration(field) + ?: checkExpressionTypeArguments() + } + + // Never patch instance initializers. Otherwise this will break a lot of lowerings. + override fun visitInstanceInitializerCall(expression: IrInstanceInitializerCall) = expression + + override fun visitExpression(expression: IrExpression) = expression.maybeThrowLinkageError { null } + + private inline fun T.maybeThrowLinkageError(computePartialLinkageCase: T.() -> PartialLinkageCase?): IrExpression = + maybeThrowLinkageError(transformer = this@ExpressionTransformer) { + computePartialLinkageCase() ?: checkExpressionType(type) // Check something that is always present in every expression. + } + + private fun IrExpression.checkExpressionType(type: IrType): PartialLinkageCase? { + return ExpressionWithUnusableClassifier(this, type.explore() ?: return null) + } + + private fun IrMemberAccessExpression<*>.checkExpressionTypeArguments(): PartialLinkageCase? { + // TODO: is it necessary to check that the number of type parameters matches the number of type arguments? + return ExpressionWithUnusableClassifier( + this, + (0 until typeArgumentsCount).firstNotNullOfOrNull { index -> getTypeArgument(index)?.explore() } ?: return null + ) + } + + private fun IrExpression.checkReferencedDeclaration( + symbol: IrSymbol?, + checkVisibility: Boolean = true + ): PartialLinkageCase? { + symbol ?: return null + + if (!symbol.isBound && !symbol.isPublicApi) { + // Such symbols might not be in SymbolTable. Just bind them in place. + stubGenerator.getDeclaration(symbol) + } + + when (val owner = symbol.owner) { + is IrDeclaration -> { + if (owner.origin == PartiallyLinkedDeclarationOrigin.MISSING_DECLARATION + || (owner as? IrLazyDeclarationBase)?.isEffectivelyMissingLazyIrDeclaration() == true + ) { + return ExpressionWithMissingDeclaration(this, symbol) + } + } + + else -> return null // Not a declaration. + } + + val partialLinkageCase = when (symbol) { + is IrClassifierSymbol -> symbol.explore()?.let { ExpressionWithUnusableClassifier(this, it) } + + is IrEnumEntrySymbol -> checkReferencedDeclaration(symbol.owner.correspondingClass?.symbol, checkVisibility = false) + + is IrPropertySymbol -> checkReferencedDeclaration(symbol.owner.getter?.symbol, checkVisibility = false) + ?: checkReferencedDeclaration(symbol.owner.setter?.symbol, checkVisibility = false) + ?: checkReferencedDeclaration(symbol.owner.backingField?.symbol, checkVisibility = false) + + else -> when (symbol) { + is IrFunctionSymbol -> with(symbol.owner) { + dispatchReceiverParameter?.type?.precalculatedUnusableClassifier() + ?: extensionReceiverParameter?.type?.precalculatedUnusableClassifier() + ?: valueParameters.firstNotNullOfOrNull { it.type.precalculatedUnusableClassifier() } + ?: returnType.precalculatedUnusableClassifier() + ?: typeParameters.firstNotNullOfOrNull { it.superTypes.precalculatedUnusableClassifier() } + } + + is IrFieldSymbol -> symbol.owner.type.precalculatedUnusableClassifier() + is IrValueSymbol -> symbol.owner.type.precalculatedUnusableClassifier() + + else -> null + }?.let { unusableClassifierInReferencedDeclaration -> + ExpressionHasDeclarationWithUnusableClassifier(this, symbol, unusableClassifierInReferencedDeclaration) + } + } + + if (partialLinkageCase != null) + return partialLinkageCase + else if (!checkVisibility) + return null + + // Do the minimal visibility check: Make sure that private declaration is not used outside its declaring entity. + // This should be enough to fix KT-54469 (cases #2 and #3). + + val signature = symbol.signature + if (signature != null + && (!signature.isPubliclyVisible || (signature as? IdSignature.CompositeSignature)?.container is IdSignature.FileSignature) + ) { + // Special case: A declaration with private signature can't be referenced from another module. So nothing to check. + return null + } + + val declaration = symbol.owner as? IrDeclarationWithVisibility ?: return null + val containingModule = PLModule.determineModuleFor(declaration) + + return when { + containingModule == currentFile.module -> { + // OK. Used in the same module. + null + } + containingModule.shouldBeSkipped -> { + // Optimization: Don't check visibility of declarations in stdlib & co. + null + } + !declaration.isEffectivelyPrivate() -> { + // Effectively public. Nothing to check. + null + } + else -> { + val declaringModule = if (declaration is IrOverridableDeclaration<*> && declaration.isFakeOverride) { + // Compute the declaring module. + declaration.allOverridden() + .firstOrNull { !it.isFakeOverride } + ?.let(PLModule::determineModuleFor) + ?: containingModule + } else + containingModule + + ExpressionHasInaccessibleDeclaration(this, symbol, declaringModule, currentFile.module) + } + } + } + + private fun IrType.precalculatedUnusableClassifier(): ExploredClassifier.Unusable? = + (this as? PartiallyLinkedMarkerType)?.unusableClassifier ?: explore() + + private fun List.precalculatedUnusableClassifier(): ExploredClassifier.Unusable? = + firstNotNullOfOrNull { it.precalculatedUnusableClassifier() } + + private fun IrExpression.checkReferencedDeclarationType( + declaration: D, + expectedDeclarationDescription: String, + checkDeclarationType: (D) -> Boolean + ): PartialLinkageCase? { + return if (!checkDeclarationType(declaration)) + ExpressionHasWrongTypeOfDeclaration(this, declaration.symbol, expectedDeclarationDescription) + else null + } + + private fun IrMemberAccessExpression.checkArgumentsAndValueParameters(function: D): PartialLinkageCase? { + val expressionEffectivelyHasDispatchReceiver = when { + dispatchReceiver != null -> true + this is IrFunctionReference -> run { + // For function references it really depends on whether the reference was obtained on a class or on an instance. + // Based on this the dispatch receiver may be null or non-null, but this is always reflected in the expression type. + // Example: + // class C { + // fun foo(i: Int): String = i.toString() + // inner class I { + // fun bar(i: Int): String = i.toString() + // } + // } + // + // fun test() { + // val a: KFunction0 = ::C // IrConstructor.dispatchReceiverParameter == null, IrFunctionReference.dispatchReceiver == null + // val b: KFunction2 = C::foo // IrSimpleFunction.dispatchReceiverParameter != null, IrFunctionReference.dispatchReceiver == null + // val c: KFunction1 = C()::foo // IrSimpleFunction.dispatchReceiverParameter != null, IrFunctionReference.dispatchReceiver != null + // val d: KFunction1 = C::I // IrConstructor.dispatchReceiverParameter != null, IrFunctionReference.dispatchReceiver == null + // val e: KFunction0 = C()::I // IrConstructor.dispatchReceiverParameter != null, IrFunctionReference.dispatchReceiver != null + // val f: KFunction2 = C.I::bar // IrSimpleFunction.dispatchReceiverParameter != null, IrFunctionReference.dispatchReceiver == null + // val g: KFunction1 = C().I()::bar // IrSimpleFunction.dispatchReceiverParameter != null, IrFunctionReference.dispatchReceiver != null + // } + val expectedDispatchReceiverClassifier: IrClassSymbol = when (symbol) { + is IrSimpleFunctionSymbol -> function.parent as? IrClass + is IrConstructorSymbol -> (function.parent as? IrClass)?.takeIf { it.isInner }?.parent as? IrClass + else -> null + }?.symbol ?: return@run false + + val referenceType: IrSimpleType = type as? IrSimpleType ?: return@run false + if (!referenceType.classifier.isKFunction() && !referenceType.classifier.isKSuspendFunction()) return@run false + + val actualDispatchReceiverClassifier: IrClassifierSymbol? = + (referenceType.arguments.firstOrNull() as? IrSimpleType)?.classifier + + expectedDispatchReceiverClassifier == actualDispatchReceiverClassifier + } + else -> false + } + val functionHasDispatchReceiver = function.dispatchReceiverParameter != null + + if (expressionEffectivelyHasDispatchReceiver != functionHasDispatchReceiver) + return MemberAccessExpressionArgumentsMismatch( + this, + expressionEffectivelyHasDispatchReceiver, + functionHasDispatchReceiver, + 0, // Does not matter here. + 0 // Does not matter here. + ) + + if (this is IrFunctionReference) { + // Function references don't contain arguments. + return null + } else if (this is IrEnumConstructorCall && (function.parent as? IrClass)?.symbol == builtIns.enumClass) { + // This is a special case. IrEnumConstructorCall don't contain arguments. + return null + } + + // Default values are not kept in value parameters of fake override/delegated/override functions. + // So we need to look up for default value across all overridden functions. + val functionsToCheckDefaultValues by lazy { + if (function !is IrSimpleFunction) + listOf(function) + else + function.allOverridden(includeSelf = true) + .filterNot { it.isFakeOverride || it.origin == IrDeclarationOrigin.DELEGATED_MEMBER } + } + + val expressionValueArgumentCount = (0 until valueArgumentsCount).count { index -> + getValueArgument(index) != null + || function.valueParameters.getOrNull(index)?.isVararg == true + || functionsToCheckDefaultValues.any { it.valueParameters.getOrNull(index)?.defaultValue != null } + } + val functionValueParameterCount = function.valueParameters.size + + return if (expressionValueArgumentCount != functionValueParameterCount) + MemberAccessExpressionArgumentsMismatch( + this, + expressionEffectivelyHasDispatchReceiver, + functionHasDispatchReceiver, + expressionValueArgumentCount, + functionValueParameterCount + ) + else + null + } + + private fun IrConstructorCall.checkNotAbstractClass(): PartialLinkageCase? { + val createdClass = symbol.owner.parentAsClass + return if (createdClass.modality == Modality.ABSTRACT || createdClass.modality == Modality.SEALED) + AbstractClassInstantiation(this, createdClass.symbol) + else + null + } + + private fun T.filterUnusableAnnotations() where T : IrMutableAnnotationContainer, T : IrElement { + if (annotations.isNotEmpty()) { + annotations = annotations.filterTo(ArrayList(annotations.size)) { annotation -> + val partialLinkageCase = with(annotation) { + checkReferencedDeclaration(symbol) + ?: checkExpressionTypeArguments() + ?: checkReferencedDeclarationType(symbol.owner.parentAsClass, "annotation class") { constructedClass -> + constructedClass.kind == ClassKind.ANNOTATION_CLASS + } + } ?: return@filterTo true + + // Just log a warning. Do not throw a linkage error as this would produce broken IR. + supportForLowerings.renderAndLogLinkageError(partialLinkageCase, this, currentFile) + + false // Drop it. + }.compact() + } + } + } + + private fun IrClassifierSymbol.explore(): ExploredClassifier.Unusable? = classifierExplorer.exploreSymbol(this) + private fun IrType.explore(): ExploredClassifier.Unusable? = classifierExplorer.exploreType(this) + + private fun IrType.toPartiallyLinkedMarkerTypeOrNull(): PartiallyLinkedMarkerType? = + explore()?.let { PartiallyLinkedMarkerType(builtIns, it) } + + private fun List.toPartiallyLinkedMarkerTypeOrNull(): PartiallyLinkedMarkerType? = + firstNotNullOfOrNull { it.toPartiallyLinkedMarkerTypeOrNull() } + + private data class DirectChildren(val statements: List, val hasPartialLinkageRuntimeError: Boolean) { + companion object { + val EMPTY = DirectChildren(emptyList(), false) + } + } + + /** + * Collects direct children statements up to the first IR p.l. error (everything after the IR p.l. error + * if effectively dead code and do not need to be kept in the IR tree). + */ + private class DirectChildrenStatementsCollector : IrElementVisitorVoid { + private val children = mutableListOf() + private var hasPartialLinkageRuntimeError = false + + fun getResult() = DirectChildren(children, hasPartialLinkageRuntimeError) + + override fun visitElement(element: IrElement) { + if (hasPartialLinkageRuntimeError) return + val statement = element as? IrStatement ?: error("Not a statement: $element") + children += statement + hasPartialLinkageRuntimeError = statement.isPartialLinkageRuntimeError() + } + } + + private sealed interface ReturnTargetContext { + val validReturnTargets: Set + + data object Empty : ReturnTargetContext { + override val validReturnTargets: Set get() = emptySet() + } + + class InFunction( + override val validReturnTargets: Set, + val function: IrFunction, + val isInlined: Boolean + ) : ReturnTargetContext + + class InFunctionBody( + override val validReturnTargets: Set + ) : ReturnTargetContext + + class InInlinedCall( + override val validReturnTargets: Set, + val inlinedLambdaArgumentsWithPermittedNonLocalReturns: Set + ) : ReturnTargetContext + } + + private inner class NonLocalReturnsPatcher(startingFile: PLFile?) : FileAwareIrElementTransformerVoid(startingFile) { + private val stack = ArrayDeque() + private val currentContext: ReturnTargetContext get() = stack.peek() ?: ReturnTargetContext.Empty + + private inline fun withContext( + getNewContext: (oldContext: ReturnTargetContext) -> ReturnTargetContext = { it }, + block: (newContext: ReturnTargetContext) -> R + ): R { + val oldContext: ReturnTargetContext = currentContext + val newContext: ReturnTargetContext = getNewContext(oldContext) + + if (newContext !== oldContext) stack.push(newContext) + + return try { + block(newContext) + } finally { + if (newContext !== oldContext) assert(stack.pop() == newContext) + } + } + + override fun visitFunction(declaration: IrFunction) = withContext( + { oldContext -> + ReturnTargetContext.InFunction( + validReturnTargets = oldContext.validReturnTargets, + function = declaration, + isInlined = oldContext is ReturnTargetContext.InInlinedCall && declaration.symbol in oldContext.inlinedLambdaArgumentsWithPermittedNonLocalReturns + ) + } + ) { super.visitFunction(declaration) } + + override fun visitBlockBody(body: IrBlockBody) = withContext( + { oldContext -> + if (oldContext !is ReturnTargetContext.InFunction || body !== oldContext.function.body) + return@withContext oldContext + + ReturnTargetContext.InFunctionBody( + validReturnTargets = if (oldContext.isInlined) + oldContext.validReturnTargets + oldContext.function.symbol // Extend the set of valid return targets. + else + setOf(oldContext.function.symbol) + ) + } + ) { super.visitBlockBody(body) } + + override fun visitCall(expression: IrCall) = withContext( + { oldContext -> + val functionSymbol = expression.symbol + val function = if (functionSymbol.isBound) functionSymbol.owner else return@withContext oldContext + if (!function.isInline) return@withContext oldContext + + fun IrValueParameter?.canHaveNonLocalReturns(): Boolean = this != null && !isCrossinline && !isNoinline + + val inlinedLambdaArgumentsWithPermittedNonLocalReturns = ArrayList(function.valueParameters.size + 1) + + fun IrExpression?.countInAsInlinedLambdaArgumentWithPermittedNonLocalReturns() { + inlinedLambdaArgumentsWithPermittedNonLocalReturns.addIfNotNull((this as? IrFunctionExpression)?.function?.symbol) + } + + if (function.extensionReceiverParameter.canHaveNonLocalReturns()) + expression.extensionReceiver.countInAsInlinedLambdaArgumentWithPermittedNonLocalReturns() + + function.valueParameters.forEachIndexed { index, valueParameter -> + if (valueParameter.canHaveNonLocalReturns()) + expression.getValueArgument(index).countInAsInlinedLambdaArgumentWithPermittedNonLocalReturns() + } + + if (inlinedLambdaArgumentsWithPermittedNonLocalReturns.isEmpty()) + return@withContext oldContext + + ReturnTargetContext.InInlinedCall( + validReturnTargets = oldContext.validReturnTargets, + inlinedLambdaArgumentsWithPermittedNonLocalReturns = inlinedLambdaArgumentsWithPermittedNonLocalReturns.toSet() + ) + } + ) { super.visitCall(expression) } + + override fun visitReturn(expression: IrReturn) = withContext { context -> + expression.maybeThrowLinkageError(transformer = this@NonLocalReturnsPatcher) { + if (returnTargetSymbol !in context.validReturnTargets) + IllegalNonLocalReturn(expression, context.validReturnTargets) + else + null + } + } + } + + private inline fun T.maybeThrowLinkageError( + transformer: FileAwareIrElementTransformerVoid, + computePartialLinkageCase: T.() -> PartialLinkageCase? + ): IrExpression { + // The codegen uses postorder traversal: Children are evaluated/executed before the containing expression. + // So it's important to patch children and insert the necessary `throw IrLinkageError(...)` calls if necessary + // before patching the containing expression itself. This would guarantee that the order of linkage errors in a program + // would conform to the natural program execution flow. + transformChildrenVoid(transformer) + + val partialLinkageCase = computePartialLinkageCase() + ?: return apply { (this as? IrContainerExpression)?.statements?.eliminateDeadCodeStatements() } + + // Collect direct children if `this` isn't an expression with branches. + val directChildren = if (!hasBranches()) + DirectChildrenStatementsCollector().also(::acceptChildrenVoid).getResult() else DirectChildren.EMPTY + + val linkageError = supportForLowerings.throwLinkageError( + partialLinkageCase, + element = this, + transformer.currentFile, + suppressWarningInCompilerOutput = false + ) + + return if (directChildren.statements.isNotEmpty()) + IrCompositeImpl(startOffset, endOffset, builtIns.nothingType, PARTIAL_LINKAGE_RUNTIME_ERROR).apply { + statements += directChildren.statements + if (!directChildren.hasPartialLinkageRuntimeError) statements += linkageError + } + else + linkageError + } + + companion object { + private fun IrDeclaration.isDirectMemberOf(unusableClassifier: ExploredClassifier.Unusable?): Boolean { + val unusableClassifierSymbol = unusableClassifier?.symbol ?: return false + val containingClassSymbol = parentClassOrNull?.symbol ?: return false + return unusableClassifierSymbol == containingClassSymbol + } + + /** + * Removes statements after the first IR p.l. error (everything after the IR p.l. error if effectively dead code and do not need + * to be kept in the IR tree). + */ + private fun MutableList.eliminateDeadCodeStatements() { + var hasPartialLinkageRuntimeError = false + removeIf { statement -> + val needToRemove = when (statement) { + is IrInstanceInitializerCall, + is IrDelegatingConstructorCall, + is IrEnumConstructorCall -> false // Don't remove essential constructor statements. + else -> hasPartialLinkageRuntimeError + } + hasPartialLinkageRuntimeError = hasPartialLinkageRuntimeError || statement.isPartialLinkageRuntimeError() + needToRemove + } + } + + private fun IrExpression.hasBranches(): Boolean = when (this) { + is IrWhen, is IrLoop, is IrTry, is IrSuspensionPoint, is IrSuspendableExpression -> true + else -> false + } + } +} diff --git a/compiler/ir/serialization.common/src/org/jetbrains/kotlin/backend/common/linkage/partial/PartiallyLinkedMarkerType.kt b/compiler/ir/serialization.common/src/org/jetbrains/kotlin/backend/common/linkage/partial/PartiallyLinkedMarkerType.kt new file mode 100644 index 00000000000..2079649500f --- /dev/null +++ b/compiler/ir/serialization.common/src/org/jetbrains/kotlin/backend/common/linkage/partial/PartiallyLinkedMarkerType.kt @@ -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() + override val classifier = builtIns.anyClass + override val nullability get() = SimpleTypeNullability.MARKED_NULLABLE + override val arguments get() = emptyList() + 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() +} diff --git a/compiler/ir/serialization.common/src/org/jetbrains/kotlin/backend/common/overrides/FakeOverrides.kt b/compiler/ir/serialization.common/src/org/jetbrains/kotlin/backend/common/overrides/FakeOverrides.kt index 298eb4da6bc..c0ec179596c 100644 --- a/compiler/ir/serialization.common/src/org/jetbrains/kotlin/backend/common/overrides/FakeOverrides.kt +++ b/compiler/ir/serialization.common/src/org/jetbrains/kotlin/backend/common/overrides/FakeOverrides.kt @@ -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 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>, - 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() 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 { + 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 { + 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) } diff --git a/compiler/ir/serialization.common/src/org/jetbrains/kotlin/backend/common/serialization/BasicIrModuleDeserializer.kt b/compiler/ir/serialization.common/src/org/jetbrains/kotlin/backend/common/serialization/BasicIrModuleDeserializer.kt index 40da205a148..4d60faf44de 100644 --- a/compiler/ir/serialization.common/src/org/jetbrains/kotlin/backend/common/serialization/BasicIrModuleDeserializer.kt +++ b/compiler/ir/serialization.common/src/org/jetbrains/kotlin/backend/common/serialization/BasicIrModuleDeserializer.kt @@ -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 } } } diff --git a/compiler/ir/serialization.common/src/org/jetbrains/kotlin/backend/common/serialization/IrBodyDeserializer.kt b/compiler/ir/serialization.common/src/org/jetbrains/kotlin/backend/common/serialization/IrBodyDeserializer.kt index 542c503d12e..6441303e4d0 100644 --- a/compiler/ir/serialization.common/src/org/jetbrains/kotlin/backend/common/serialization/IrBodyDeserializer.kt +++ b/compiler/ir/serialization.common/src/org/jetbrains/kotlin/backend/common/serialization/IrBodyDeserializer.kt @@ -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() @@ -149,7 +152,7 @@ class IrBodyDeserializer( private fun deserializeBlock(proto: ProtoBlock, start: Int, end: Int, type: IrType): IrBlock { val statements = mutableListOf() 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( + 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(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(proto.symbol, FUNCTION_SYMBOL) + val superSymbol = deserializeTypedSymbolWhen(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() 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(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(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( + proto.symbol, + fallbackSymbolKind = /* just the first possible option */ FUNCTION_SYMBOL + ) + val origin = deserializeIrStatementOrigin(proto.hasOriginName()) { proto.originName } + val reflectionTarget = deserializeTypedSymbolWhen( + 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(access.symbol, FIELD_SYMBOL) + val origin = deserializeIrStatementOrigin(proto.hasOriginName()) { proto.originName } + val superQualifier = deserializeTypedSymbolWhen(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(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(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(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(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(proto.delegate, fallbackSymbolKind = null) + val getter = deserializeTypedSymbol(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(proto.hasSetter(), FUNCTION_SYMBOL) { proto.setter } + val symbol = deserializeTypedSymbol(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(proto.symbol, PROPERTY_SYMBOL) + val field = deserializeTypedSymbolWhen(proto.hasField(), FIELD_SYMBOL) { proto.field } + val getter = deserializeTypedSymbolWhen(proto.hasGetter(), FUNCTION_SYMBOL) { proto.getter } + val setter = deserializeTypedSymbolWhen(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( + 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(access.symbol, FIELD_SYMBOL) + val superQualifier = deserializeTypedSymbolWhen(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(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() - 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 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 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() diff --git a/compiler/ir/serialization.common/src/org/jetbrains/kotlin/backend/common/serialization/IrDeclarationDeserializer.kt b/compiler/ir/serialization.common/src/org/jetbrains/kotlin/backend/common/serialization/IrDeclarationDeserializer.kt index ad3c12d7413..a69620b1868 100644 --- a/compiler/ir/serialization.common/src/org/jetbrains/kotlin/backend/common/serialization/IrDeclarationDeserializer.kt +++ b/compiler/ir/serialization.common/src/org/jetbrains/kotlin/backend/common/serialization/IrDeclarationDeserializer.kt @@ -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(deserializeIrSymbolAndRemap(proto.classifier)) + val symbol = deserializeIrSymbolAndRemap(proto.classifier) + .checkSymbolType(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(deserializeIrSymbolAndRemap(proto.classifier)) + val symbol = deserializeIrSymbolAndRemap(proto.classifier) + .checkSymbolType(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(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(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(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(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(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 withDeserializedIrFunctionBase( + private inline fun 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(symbol) - + internal fun deserializeIrFunction(proto: ProtoFunction, setParent: Boolean = true): IrSimpleFunction = + withDeserializedIrFunctionBase( + 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(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(symbol) + withDeserializedIrFunctionBase( + 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(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(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(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 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 + } } diff --git a/compiler/ir/serialization.common/src/org/jetbrains/kotlin/backend/common/serialization/IrFileDeserializer.kt b/compiler/ir/serialization.common/src/org/jetbrains/kotlin/backend/common/serialization/IrFileDeserializer.kt index b45ace607a0..532bf4b04b5 100644 --- a/compiler/ir/serialization.common/src/org/jetbrains/kotlin/backend/common/serialization/IrFileDeserializer.kt +++ b/compiler/ir/serialization.common/src/org/jetbrains/kotlin/backend/common/serialization/IrFileDeserializer.kt @@ -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) diff --git a/compiler/ir/serialization.common/src/org/jetbrains/kotlin/backend/common/serialization/IrModuleDeserializer.kt b/compiler/ir/serialization.common/src/org/jetbrains/kotlin/backend/common/serialization/IrModuleDeserializer.kt index f0590c76b06..b369b496c46 100644 --- a/compiler/ir/serialization.common/src/org/jetbrains/kotlin/backend/common/serialization/IrModuleDeserializer.kt +++ b/compiler/ir/serialization.common/src/org/jetbrains/kotlin/backend/common/serialization/IrModuleDeserializer.kt @@ -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() diff --git a/compiler/ir/serialization.common/src/org/jetbrains/kotlin/backend/common/serialization/IrSymbolDeserializer.kt b/compiler/ir/serialization.common/src/org/jetbrains/kotlin/backend/common/serialization/IrSymbolDeserializer.kt index 152c50b8b05..7831e99f22d 100644 --- a/compiler/ir/serialization.common/src/org/jetbrains/kotlin/backend/common/serialization/IrSymbolDeserializer.kt +++ b/compiler/ir/serialization.common/src/org/jetbrains/kotlin/backend/common/serialization/IrSymbolDeserializer.kt @@ -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 = 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") } diff --git a/compiler/ir/serialization.common/src/org/jetbrains/kotlin/backend/common/serialization/KotlinIrLinker.kt b/compiler/ir/serialization.common/src/org/jetbrains/kotlin/backend/common/serialization/KotlinIrLinker.kt index 5518d8167df..975a0ad8f61 100644 --- a/compiler/ir/serialization.common/src/org/jetbrains/kotlin/backend/common/serialization/KotlinIrLinker.kt +++ b/compiler/ir/serialization.common/src/org/jetbrains/kotlin/backend/common/serialization/KotlinIrLinker.kt @@ -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() - val topLevelActualUniqItToDeserializer = mutableMapOf() + val expectIdSignatureToActualIdSignature = mutableMapOf() + val topLevelActualIdSignatureToModuleDeserializer = mutableMapOf() internal val expectSymbols = mutableMapOf() internal val actualSymbols = mutableMapOf() @@ -53,10 +53,8 @@ abstract class KotlinIrLinker( private lateinit var linkerExtensions: Collection - 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 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) { diff --git a/compiler/ir/serialization.common/src/org/jetbrains/kotlin/backend/common/serialization/unlinked/PartialLinkageSupport.kt b/compiler/ir/serialization.common/src/org/jetbrains/kotlin/backend/common/serialization/unlinked/PartialLinkageSupport.kt deleted file mode 100644 index 8d8875b9bc2..00000000000 --- a/compiler/ir/serialization.common/src/org/jetbrains/kotlin/backend/common/serialization/unlinked/PartialLinkageSupport.kt +++ /dev/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) - - 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) = Unit - } - } -} diff --git a/compiler/ir/serialization.common/src/org/jetbrains/kotlin/backend/common/serialization/unlinked/PartialLinkageSupportImpl.kt b/compiler/ir/serialization.common/src/org/jetbrains/kotlin/backend/common/serialization/unlinked/PartialLinkageSupportImpl.kt deleted file mode 100644 index 512219c9967..00000000000 --- a/compiler/ir/serialization.common/src/org/jetbrains/kotlin/backend/common/serialization/unlinked/PartialLinkageSupportImpl.kt +++ /dev/null @@ -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() - for (clazz in entries.keys) { - if (clazz.symbol.isUnlinkedClassifier(visited = hashSetOf())) { - toRemove += clazz - } - } - - entries -= toRemove - } - - private fun IrType.isUnlinkedType(visited: MutableSet): 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): 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) { - 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) } - } -} diff --git a/compiler/ir/serialization.common/src/org/jetbrains/kotlin/backend/common/serialization/unlinked/UnlinkedDeclarationsProcessor.kt b/compiler/ir/serialization.common/src/org/jetbrains/kotlin/backend/common/serialization/unlinked/UnlinkedDeclarationsProcessor.kt deleted file mode 100644 index 49dc8bccb50..00000000000 --- a/compiler/ir/serialization.common/src/org/jetbrains/kotlin/backend/common/serialization/unlinked/UnlinkedDeclarationsProcessor.kt +++ /dev/null @@ -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 = 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 - ): 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) = - renderError(this@composeUnlinkedSymbolsErrorMessage, unlinkedSymbols) - - private fun IrDeclaration.throwLinkageError(unlinkedSymbols: Collection = emptyList()): IrCall = - throwLinkageError( - messages = listOf(composeUnlinkedSymbolsErrorMessage(unlinkedSymbols)), - location = location() - ) - - private fun IrElement.throwLinkageError(messages: List, 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) { - 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 IrOverridableDeclaration.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 - } -} diff --git a/compiler/ir/serialization.common/src/org/jetbrains/kotlin/backend/common/serialization/unlinked/UnlinkedIrElementRenderer.kt b/compiler/ir/serialization.common/src/org/jetbrains/kotlin/backend/common/serialization/unlinked/UnlinkedIrElementRenderer.kt deleted file mode 100644 index 99948df7d39..00000000000 --- a/compiler/ir/serialization.common/src/org/jetbrains/kotlin/backend/common/serialization/unlinked/UnlinkedIrElementRenderer.kt +++ /dev/null @@ -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): 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. - else -> 1 - } - effectiveSignature.guessNameBySignature(nameSegmentsToPickUp) - } - ?: boundOwnerDeclarationOrNull?.getNameWithAssert()?.asString() -} - -private val IrSymbol.anySignature: IdSignature? - get() = signature ?: privateSignature - -private const val UNKNOWN_SYMBOL = "" -private const val 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 -} diff --git a/compiler/ir/serialization.common/src/org/jetbrains/kotlin/backend/common/serialization/unlinked/UsedClassifierSymbols.kt b/compiler/ir/serialization.common/src/org/jetbrains/kotlin/backend/common/serialization/unlinked/UsedClassifierSymbols.kt deleted file mode 100644 index 1f2275465c8..00000000000 --- a/compiler/ir/serialization.common/src/org/jetbrains/kotlin/backend/common/serialization/unlinked/UsedClassifierSymbols.kt +++ /dev/null @@ -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() - private val patchedSymbols = HashSet() // 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 - } -} diff --git a/compiler/ir/serialization.js/src/org/jetbrains/kotlin/ir/backend/js/klib.kt b/compiler/ir/serialization.js/src/org/jetbrains/kotlin/ir/backend/js/klib.kt index 7c5596fcc06..5ebe47b7a61 100644 --- a/compiler/ir/serialization.js/src/org/jetbrains/kotlin/ir/backend/js/klib.kt +++ b/compiler/ir/serialization.js/src/org/jetbrains/kotlin/ir/backend/js/klib.kt @@ -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.* diff --git a/compiler/ir/serialization.js/src/org/jetbrains/kotlin/ir/backend/js/lower/serialization/ir/JsIrLinker.kt b/compiler/ir/serialization.js/src/org/jetbrains/kotlin/ir/backend/js/lower/serialization/ir/JsIrLinker.kt index 5089096ba7c..29e64ce62d8 100644 --- a/compiler/ir/serialization.js/src/org/jetbrains/kotlin/ir/backend/js/lower/serialization/ir/JsIrLinker.kt +++ b/compiler/ir/serialization.js/src/org/jetbrains/kotlin/ir/backend/js/lower/serialization/ir/JsIrLinker.kt @@ -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 = diff --git a/compiler/ir/serialization.jvm/src/org/jetbrains/kotlin/backend/jvm/serialization/deserializeLazyDeclarations.kt b/compiler/ir/serialization.jvm/src/org/jetbrains/kotlin/backend/jvm/serialization/deserializeLazyDeclarations.kt index 6c1865d1fe6..15bc3c2f07d 100644 --- a/compiler/ir/serialization.jvm/src/org/jetbrains/kotlin/backend/jvm/serialization/deserializeLazyDeclarations.kt +++ b/compiler/ir/serialization.jvm/src/org/jetbrains/kotlin/backend/jvm/serialization/deserializeLazyDeclarations.kt @@ -85,6 +85,7 @@ fun deserializeFromByteArray( DefaultFakeOverrideClassFilter, fakeOverrideBuilder, compatibilityMode = CompatibilityMode.CURRENT, + partialLinkageEnabled = false ) for (declarationProto in irProto.declarationList) { deserializer.deserializeDeclaration(declarationProto, setParent = false) diff --git a/compiler/tests-common/tests/org/jetbrains/kotlin/ir/AbstractIrGeneratorTestCase.kt b/compiler/tests-common/tests/org/jetbrains/kotlin/ir/AbstractIrGeneratorTestCase.kt index 299e6a0707d..64ec0c9e1ff 100644 --- a/compiler/tests-common/tests/org/jetbrains/kotlin/ir/AbstractIrGeneratorTestCase.kt +++ b/compiler/tests-common/tests/org/jetbrains/kotlin/ir/AbstractIrGeneratorTestCase.kt @@ -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 diff --git a/compiler/tests-common/tests/org/jetbrains/kotlin/klib/AbstractKlibIrTextTestCase.kt b/compiler/tests-common/tests/org/jetbrains/kotlin/klib/AbstractKlibIrTextTestCase.kt index 80ffe0d68bb..5e76e37567b 100644 --- a/compiler/tests-common/tests/org/jetbrains/kotlin/klib/AbstractKlibIrTextTestCase.kt +++ b/compiler/tests-common/tests/org/jetbrains/kotlin/klib/AbstractKlibIrTextTestCase.kt @@ -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 diff --git a/js/js.tests/test/org/jetbrains/kotlin/benchmarks/GenerateIrRuntime.kt b/js/js.tests/test/org/jetbrains/kotlin/benchmarks/GenerateIrRuntime.kt index bf0237e36a2..939c8af324a 100644 --- a/js/js.tests/test/org/jetbrains/kotlin/benchmarks/GenerateIrRuntime.kt +++ b/js/js.tests/test/org/jetbrains/kotlin/benchmarks/GenerateIrRuntime.kt @@ -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 diff --git a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/Context.kt b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/Context.kt index 8d6d61f29bf..48ea2e9a044 100644 --- a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/Context.kt +++ b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/Context.kt @@ -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) { diff --git a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/KonanBackendContext.kt b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/KonanBackendContext.kt index 95e6278bde3..c072ea70f08 100644 --- a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/KonanBackendContext.kt +++ b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/KonanBackendContext.kt @@ -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 diff --git a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/KonanConfig.kt b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/KonanConfig.kt index ebb6ee3ee76..edfbf03a7f4 100644 --- a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/KonanConfig.kt +++ b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/KonanConfig.kt @@ -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") diff --git a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/PsiToIr.kt b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/PsiToIr.kt index cfbc2760f17..80561eef955 100644 --- a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/PsiToIr.kt +++ b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/PsiToIr.kt @@ -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 diff --git a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/SetupConfiguration.kt b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/SetupConfiguration.kt index df09f7cd2b5..9516a4fc3e0 100644 --- a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/SetupConfiguration.kt +++ b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/SetupConfiguration.kt @@ -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) diff --git a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/InnerClassLowering.kt b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/InnerClassLowering.kt index 8cdf0214c95..11b050af463 100644 --- a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/InnerClassLowering.kt +++ b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/InnerClassLowering.kt @@ -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}") diff --git a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/NativeAddContinuationToFunctionCallsLowering.kt b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/NativeAddContinuationToFunctionCallsLowering.kt index 9da50eb6eb3..5dc97f485db 100644 --- a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/NativeAddContinuationToFunctionCallsLowering.kt +++ b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/NativeAddContinuationToFunctionCallsLowering.kt @@ -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" } - }!! - } - } -} \ No newline at end of file + override fun IrSimpleFunction.isContinuationItself(): Boolean = overrides(context.ir.symbols.invokeSuspendFunction.owner) +} diff --git a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/serialization/KonanIrlinker.kt b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/serialization/KonanIrlinker.kt index 36c1b088ec4..5f0a0b12a41 100644 --- a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/serialization/KonanIrlinker.kt +++ b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/serialization/KonanIrlinker.kt @@ -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 diff --git a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/serialization/KonanUserVisibleIrModulesSupport.kt b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/serialization/KonanUserVisibleIrModulesSupport.kt index 8884d0db11e..f8f9f4354b6 100644 --- a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/serialization/KonanUserVisibleIrModulesSupport.kt +++ b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/serialization/KonanUserVisibleIrModulesSupport.kt @@ -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 diff --git a/plugins/android-extensions/android-extensions-compiler/src/org/jetbrains/kotlin/android/synthetic/codegen/AndroidIrExtension.kt b/plugins/android-extensions/android-extensions-compiler/src/org/jetbrains/kotlin/android/synthetic/codegen/AndroidIrExtension.kt index f0effe3d7a4..84d46ef4c8c 100644 --- a/plugins/android-extensions/android-extensions-compiler/src/org/jetbrains/kotlin/android/synthetic/codegen/AndroidIrExtension.kt +++ b/plugins/android-extensions/android-extensions-compiler/src/org/jetbrains/kotlin/android/synthetic/codegen/AndroidIrExtension.kt @@ -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