From 2a4d8800374578c1aa9ec9c996b393a98f5a6e3b Mon Sep 17 00:00:00 2001 From: Dmitriy Dolovov Date: Mon, 17 Oct 2022 18:57:58 +0200 Subject: [PATCH] [PL] Deep rework of the partial linkage 1. Leaving no unbound symbols in the IR tree, KT-54491: - All unbound symbols are bound to synthetic stub declarations - Improved detection of the root cause for every partially linked classifier - Improved error messages 2. Visibility valiation, KT-54469 3. Always check deserialized symbols: If the deserialized symbol mismatches the symbol kind at the call site in the deserializer then generate and reference another symbol with the same signature. In case PL is off, just throw IrSymbolTypeMismatchException. 4. Handle class inheritance violation: - Detect illegal inheritance (ex: inheriting from a final class) - Detect invalid constructor delegation (ex: delegating to another class than the direct superclass) - Simplification: Reduce the number of PartialLinkageCase subclasses - Reworked error message generation to have shorter and clearer messages 5. Handle class transformations and all known side-effects, examples: - nested <-> inner - class <-> enum/object - adding/removing subclasses of sealed class - adding/removing enum entries 6. Check direct instantiation of abstract class. Such instantiation could be possible if a class was non-abstract in the previous version of a library. 7. Handle unlinked annotations on declarations. Such annotations are removed from the IR. The appropriate compiler error message is produced for every individual case. 8. Handle value argument count mismatch at call sites 9. Handle calling suspend function from non-suspend context. This could happen if a suspen function was non-suspend in the previous version of a library. 10. Handle overriding inline callables. Only the leaf final callable can be marked with `inline`. 11. Detect illegal non-local returns from noinline/crossinline lambdas. --- .../jetbrains/kotlin/cli/js/klib/irForKlib.kt | 2 +- .../backend/common/CommonBackendContext.kt | 4 + .../jetbrains/kotlin/backend/common/ir/Ir.kt | 1 - .../common/lower/LocalDeclarationsLowering.kt | 13 +- .../kotlin/backend/common/lower/LowerUtils.kt | 43 +- .../AddContinuationToFunctionCallsLowering.kt | 53 +- .../ir/backend/js/JsIrBackendContext.kt | 12 +- .../kotlin/ir/backend/js/compiler.kt | 2 +- .../ir/backend/js/ic/JsIrLinkerLoader.kt | 2 +- .../AddContinuationToFunctionCallsLowering.kt | 20 +- .../kotlin/backend/jvm/JvmIrCodegenFactory.kt | 2 +- .../jetbrains/kotlin/backend/wasm/compiler.kt | 4 +- .../ir/expressions/IrStatementOrigin.kt | 2 + .../ir/linkage/partial/ExploredClassifier.kt | 59 + .../IrUnimplementedOverridesStrategy.kt | 6 +- .../ir/linkage/partial/PartialLinkageCase.kt | 154 +++ .../PartialLinkageSupportForLowerings.kt | 33 + .../ir/linkage/partial/PartialLinkageUtils.kt | 136 +++ .../PartiallyLinkedDeclarationOrigin.kt | 20 + .../CopyIrTreeWithSymbolsForFakeOverrides.kt | 1 + .../kotlin/ir/overrides/FakeOverrideCopier.kt | 5 +- .../kotlin/ir/overrides/IrOverridingUtil.kt | 287 ++--- .../kotlin/ir/overrides/IrVisibilityUtil.kt | 26 +- .../kotlin/ir/util/ConstantValueGenerator.kt | 13 +- .../org/jetbrains/kotlin/ir/util/IrUtils.kt | 39 +- .../issues}/IrDeserializationExceptions.kt | 2 +- .../issues}/KotlinIrLinkerIssues.kt | 8 +- .../issues}/UserVisibleIrModulesSupport.kt | 2 +- .../linkerissues => linkage/issues}/checks.kt | 29 +- .../linkage/partial/ClassifierExplorer.kt | 309 +++++ .../linkage/partial/ExploredClassifiers.kt | 27 + .../IrUnimplementedOverridesStrategy.kt | 26 + .../MissingDeclarationStubGenerator.kt | 162 +++ .../partial/PartialLinkageErrorMessages.kt | 563 +++++++++ .../partial/PartialLinkageSupportForLinker.kt | 49 + .../PartialLinkageSupportForLinkerImpl.kt | 66 ++ .../PartialLinkageSupportForLoweringsImpl.kt | 82 ++ .../linkage/partial/PartialLinkageUtils.kt | 94 ++ .../partial/PartiallyLinkedIrTreePatcher.kt | 1003 +++++++++++++++++ .../partial/PartiallyLinkedMarkerType.kt | 31 + .../backend/common/overrides/FakeOverrides.kt | 207 ++-- .../BasicIrModuleDeserializer.kt | 8 +- .../serialization/IrBodyDeserializer.kt | 165 +-- .../IrDeclarationDeserializer.kt | 125 +- .../serialization/IrFileDeserializer.kt | 3 +- .../serialization/IrModuleDeserializer.kt | 4 +- .../serialization/IrSymbolDeserializer.kt | 6 +- .../common/serialization/KotlinIrLinker.kt | 43 +- .../unlinked/PartialLinkageSupport.kt | 38 - .../unlinked/PartialLinkageSupportImpl.kt | 182 --- .../unlinked/UnlinkedDeclarationsProcessor.kt | 385 ------- .../unlinked/UnlinkedIrElementRenderer.kt | 221 ---- .../unlinked/UsedClassifierSymbols.kt | 47 - .../jetbrains/kotlin/ir/backend/js/klib.kt | 2 +- .../js/lower/serialization/ir/JsIrLinker.kt | 2 +- .../deserializeLazyDeclarations.kt | 1 + .../kotlin/ir/AbstractIrGeneratorTestCase.kt | 2 +- .../kotlin/klib/AbstractKlibIrTextTestCase.kt | 2 +- .../kotlin/benchmarks/GenerateIrRuntime.kt | 2 +- .../jetbrains/kotlin/backend/konan/Context.kt | 8 + .../backend/konan/KonanBackendContext.kt | 5 - .../kotlin/backend/konan/KonanConfig.kt | 7 +- .../jetbrains/kotlin/backend/konan/PsiToIr.kt | 4 +- .../backend/konan/SetupConfiguration.kt | 2 +- .../backend/konan/lower/InnerClassLowering.kt | 5 +- ...eAddContinuationToFunctionCallsLowering.kt | 17 +- .../konan/serialization/KonanIrlinker.kt | 32 +- .../KonanUserVisibleIrModulesSupport.kt | 2 +- .../synthetic/codegen/AndroidIrExtension.kt | 4 +- 69 files changed, 3526 insertions(+), 1397 deletions(-) create mode 100644 compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/linkage/partial/ExploredClassifier.kt rename compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/{overrides => linkage/partial}/IrUnimplementedOverridesStrategy.kt (85%) create mode 100644 compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/linkage/partial/PartialLinkageCase.kt create mode 100644 compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/linkage/partial/PartialLinkageSupportForLowerings.kt create mode 100644 compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/linkage/partial/PartialLinkageUtils.kt create mode 100644 compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/linkage/partial/PartiallyLinkedDeclarationOrigin.kt rename compiler/ir/serialization.common/src/org/jetbrains/kotlin/backend/common/{serialization/linkerissues => linkage/issues}/IrDeserializationExceptions.kt (93%) rename compiler/ir/serialization.common/src/org/jetbrains/kotlin/backend/common/{serialization/linkerissues => linkage/issues}/KotlinIrLinkerIssues.kt (98%) rename compiler/ir/serialization.common/src/org/jetbrains/kotlin/backend/common/{serialization/linkerissues => linkage/issues}/UserVisibleIrModulesSupport.kt (99%) rename compiler/ir/serialization.common/src/org/jetbrains/kotlin/backend/common/{serialization/linkerissues => linkage/issues}/checks.kt (60%) create mode 100644 compiler/ir/serialization.common/src/org/jetbrains/kotlin/backend/common/linkage/partial/ClassifierExplorer.kt create mode 100644 compiler/ir/serialization.common/src/org/jetbrains/kotlin/backend/common/linkage/partial/ExploredClassifiers.kt create mode 100644 compiler/ir/serialization.common/src/org/jetbrains/kotlin/backend/common/linkage/partial/IrUnimplementedOverridesStrategy.kt create mode 100644 compiler/ir/serialization.common/src/org/jetbrains/kotlin/backend/common/linkage/partial/MissingDeclarationStubGenerator.kt create mode 100644 compiler/ir/serialization.common/src/org/jetbrains/kotlin/backend/common/linkage/partial/PartialLinkageErrorMessages.kt create mode 100644 compiler/ir/serialization.common/src/org/jetbrains/kotlin/backend/common/linkage/partial/PartialLinkageSupportForLinker.kt create mode 100644 compiler/ir/serialization.common/src/org/jetbrains/kotlin/backend/common/linkage/partial/PartialLinkageSupportForLinkerImpl.kt create mode 100644 compiler/ir/serialization.common/src/org/jetbrains/kotlin/backend/common/linkage/partial/PartialLinkageSupportForLoweringsImpl.kt create mode 100644 compiler/ir/serialization.common/src/org/jetbrains/kotlin/backend/common/linkage/partial/PartialLinkageUtils.kt create mode 100644 compiler/ir/serialization.common/src/org/jetbrains/kotlin/backend/common/linkage/partial/PartiallyLinkedIrTreePatcher.kt create mode 100644 compiler/ir/serialization.common/src/org/jetbrains/kotlin/backend/common/linkage/partial/PartiallyLinkedMarkerType.kt delete mode 100644 compiler/ir/serialization.common/src/org/jetbrains/kotlin/backend/common/serialization/unlinked/PartialLinkageSupport.kt delete mode 100644 compiler/ir/serialization.common/src/org/jetbrains/kotlin/backend/common/serialization/unlinked/PartialLinkageSupportImpl.kt delete mode 100644 compiler/ir/serialization.common/src/org/jetbrains/kotlin/backend/common/serialization/unlinked/UnlinkedDeclarationsProcessor.kt delete mode 100644 compiler/ir/serialization.common/src/org/jetbrains/kotlin/backend/common/serialization/unlinked/UnlinkedIrElementRenderer.kt delete mode 100644 compiler/ir/serialization.common/src/org/jetbrains/kotlin/backend/common/serialization/unlinked/UsedClassifierSymbols.kt 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