From 8de4eb798c67cc9231cb7425d90695800f40d2ab Mon Sep 17 00:00:00 2001 From: Dmitriy Dolovov Date: Wed, 27 Apr 2022 20:15:55 +0300 Subject: [PATCH] [IR] Fix removal-of-abstract-fun IR linkage case ^KT-50771 --- .../ir/util/DeepCopyIrTreeWithSymbols.kt | 7 +- .../unlinked/ImplementedFakeOverrides.kt | 90 ++++++++++ .../unlinked/UnlinkedDeclarationsProcessor.kt | 169 +++++++++++++----- 3 files changed, 218 insertions(+), 48 deletions(-) create mode 100644 compiler/ir/serialization.common/src/org/jetbrains/kotlin/backend/common/serialization/unlinked/ImplementedFakeOverrides.kt diff --git a/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/util/DeepCopyIrTreeWithSymbols.kt b/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/util/DeepCopyIrTreeWithSymbols.kt index 2c95e5f7378..baaa67bfa11 100644 --- a/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/util/DeepCopyIrTreeWithSymbols.kt +++ b/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/util/DeepCopyIrTreeWithSymbols.kt @@ -24,8 +24,13 @@ import org.jetbrains.kotlin.name.Name inline fun T.deepCopyWithSymbols( initialParent: IrDeclarationParent? = null, createCopier: (SymbolRemapper, TypeRemapper) -> DeepCopyIrTreeWithSymbols = ::DeepCopyIrTreeWithSymbols +): T = deepCopyWithSymbols(initialParent, DeepCopySymbolRemapper(), createCopier) + +inline fun T.deepCopyWithSymbols( + initialParent: IrDeclarationParent?, + symbolRemapper: DeepCopySymbolRemapper, + createCopier: (SymbolRemapper, TypeRemapper) -> DeepCopyIrTreeWithSymbols = ::DeepCopyIrTreeWithSymbols ): T { - val symbolRemapper = DeepCopySymbolRemapper() acceptVoid(symbolRemapper) val typeRemapper = DeepCopyTypeRemapper(symbolRemapper) return transform(createCopier(symbolRemapper, typeRemapper), null).patchDeclarationParents(initialParent) as T diff --git a/compiler/ir/serialization.common/src/org/jetbrains/kotlin/backend/common/serialization/unlinked/ImplementedFakeOverrides.kt b/compiler/ir/serialization.common/src/org/jetbrains/kotlin/backend/common/serialization/unlinked/ImplementedFakeOverrides.kt new file mode 100644 index 00000000000..28afde6ecdc --- /dev/null +++ b/compiler/ir/serialization.common/src/org/jetbrains/kotlin/backend/common/serialization/unlinked/ImplementedFakeOverrides.kt @@ -0,0 +1,90 @@ +/* + * 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.ir.declarations.* +import org.jetbrains.kotlin.ir.symbols.IrSimpleFunctionSymbol +import org.jetbrains.kotlin.ir.util.* + +internal inline fun > T.deepCopyWithImplementedFakeOverrides(): T { + val clazz = parentAsClass + + return deepCopyWithSymbols(clazz, DeepCopySymbolRemapperPreservingSignatures()) { symbolRemapper, typeRemapper -> + ImplementedFakeOverrideCopier(clazz, symbolRemapper, typeRemapper) + } +} + +internal class ImplementedFakeOverrideCopier( + private val clazz: IrClass, + private val symbolRemapper: SymbolRemapper, + private val typeRemapper: TypeRemapper +) : DeepCopyIrTreeWithSymbols(symbolRemapper, typeRemapper) { + override fun visitProperty(declaration: IrProperty): IrProperty = + declaration.factory.createProperty( + startOffset = declaration.startOffset, + endOffset = declaration.endOffset, + origin = MISSING_ABSTRACT_CALLABLE_MEMBER_IMPLEMENTATION, // Customized. + symbol = symbolRemapper.getDeclaredProperty(declaration.symbol), + name = declaration.name, + visibility = declaration.visibility, + modality = clazz.modality, // Customized. + isVar = declaration.isVar, + isConst = declaration.isConst, + isLateinit = declaration.isLateinit, + isDelegated = declaration.isDelegated, + isExternal = declaration.isExternal, + isExpect = declaration.isExpect, + isFakeOverride = false, // Customized. + containerSource = declaration.containerSource, + ).apply { + overriddenSymbols = declaration.overriddenSymbols.map { symbolRemapper.getReferencedProperty(it) } + copyAttributes(declaration) + transformAnnotations(declaration) + backingField = declaration.backingField?.transform()?.also { it.correspondingPropertySymbol = symbol } + getter = declaration.getter?.transform()?.also { it.correspondingPropertySymbol = symbol } + setter = declaration.setter?.transform()?.also { it.correspondingPropertySymbol = symbol } + } + + override fun visitSimpleFunction(declaration: IrSimpleFunction): IrSimpleFunction = + declaration.factory.createFunction( + startOffset = declaration.startOffset, + endOffset = declaration.endOffset, + origin = MISSING_ABSTRACT_CALLABLE_MEMBER_IMPLEMENTATION, // Customized. + symbol = symbolRemapper.getDeclaredFunction(declaration.symbol), + name = declaration.name, + visibility = declaration.visibility, + modality = clazz.modality, // Customized. + returnType = declaration.returnType, + isInline = declaration.isInline, + isExternal = declaration.isExternal, + isTailrec = declaration.isTailrec, + isSuspend = declaration.isSuspend, + isOperator = declaration.isOperator, + isInfix = declaration.isInfix, + isExpect = declaration.isExpect, + isFakeOverride = false, // Customized. + containerSource = declaration.containerSource, + ).apply { + overriddenSymbols = declaration.overriddenSymbols.map { symbolRemapper.getReferencedFunction(it) as IrSimpleFunctionSymbol } + contextReceiverParametersCount = declaration.contextReceiverParametersCount + copyAttributes(declaration) + transformAnnotations(this) + copyTypeParametersFrom(declaration) + typeRemapper.withinScope(this) { + dispatchReceiverParameter = declaration.dispatchReceiverParameter?.transform() + extensionReceiverParameter = declaration.extensionReceiverParameter?.transform() + returnType = typeRemapper.remapType(declaration.returnType) + valueParameters = declaration.valueParameters.transform() + body = factory.createBlockBody( + declaration.body?.startOffset ?: declaration.startOffset, + declaration.body?.endOffset ?: declaration.endOffset + ) // the body should be empty + } + } +} + +private val MISSING_ABSTRACT_CALLABLE_MEMBER_IMPLEMENTATION = + object : IrDeclarationOriginImpl("MISSING_ABSTRACT_CALLABLE_MEMBER_IMPLEMENTATION", isSynthetic = true) {} 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 index 18bc98c6731..0fe11b2ac30 100644 --- 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 @@ -6,6 +6,7 @@ package org.jetbrains.kotlin.backend.common.serialization.unlinked import org.jetbrains.kotlin.backend.common.serialization.unlinked.UnlinkedDeclarationsSupport.UnlinkedMarkerTypeHandler +import org.jetbrains.kotlin.descriptors.Modality import org.jetbrains.kotlin.ir.IrBuiltIns import org.jetbrains.kotlin.ir.IrElement import org.jetbrains.kotlin.ir.IrStatement @@ -19,11 +20,9 @@ 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.util.IrMessageLogger -import org.jetbrains.kotlin.ir.util.fileOrNull -import org.jetbrains.kotlin.ir.util.fqNameForIrSerialization -import org.jetbrains.kotlin.ir.util.fqNameWhenAvailable +import org.jetbrains.kotlin.ir.util.* import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid +import org.jetbrains.kotlin.name.FqName internal class UnlinkedDeclarationsProcessor( private val builtIns: IrBuiltIns, @@ -33,7 +32,7 @@ internal class UnlinkedDeclarationsProcessor( ) { companion object { - val errorOrigin = object : IrStatementOriginImpl("LINKAGE ERROR") {} + private val errorOrigin = object : IrStatementOriginImpl("LINKAGE ERROR") {} } fun addLinkageErrorIntoUnlinkedClasses() { @@ -54,7 +53,7 @@ internal class UnlinkedDeclarationsProcessor( } anonInitializer.body.statements.clear() - klass.reportWarning("Class", klass.fqNameForIrSerialization.asString()) + klass.reportUnlinkedSymbolsWarning("Class", klass.fqNameForIrSerialization) anonInitializer.body.statements.add(klass.throwLinkageError(klass.symbol)) @@ -75,8 +74,8 @@ internal class UnlinkedDeclarationsProcessor( return IrMessageLogger.Location("$module @ $fileName", lineNumber, columnNumber) } - private fun IrDeclaration.reportWarning(kind: String, fqn: String) { - reportWarning("$kind declaration $fqn contains unlinked symbols", location()) + private fun IrDeclaration.reportUnlinkedSymbolsWarning(kind: String, fqn: FqName) { + reportWarning("$kind declaration ${fqn.asString()} contains unlinked symbols", location()) } private fun reportWarning(message: String, location: IrMessageLogger.Location?) { @@ -86,54 +85,38 @@ internal class UnlinkedDeclarationsProcessor( fun signatureTransformer(): IrElementTransformerVoid = SignatureTransformer() private inner class SignatureTransformer : IrElementTransformerVoid() { + private val implementedFakeOverrideProperties = hashSetOf() + + private val IrFunction.isAccessorOfImplementedFakeOverrideProperty: Boolean + get() = (this as? IrSimpleFunction)?.correspondingPropertySymbol?.owner in implementedFakeOverrideProperties + + override fun visitProperty(declaration: IrProperty): IrStatement { + val newProperty = declaration.replaceIfUnimplementedFakeOverride() + + val isImplementedFakeOverride = newProperty != declaration + if (isImplementedFakeOverride) implementedFakeOverrideProperties += newProperty + + newProperty.transformChildrenVoid() + + if (isImplementedFakeOverride) implementedFakeOverrideProperties -= newProperty + + return newProperty + } override fun visitFunction(declaration: IrFunction): IrStatement { - var linked = true - fun IrValueParameter.fixType() { - if (type.isUnlinked()) { - linked = false - type = unlinkedMarkerTypeHandler.unlinkedMarkerType - defaultValue = null - } - varargElementType?.let { - if (it.isUnlinked()) { - varargElementType = unlinkedMarkerTypeHandler.unlinkedMarkerType - } - } - } - declaration.dispatchReceiverParameter?.fixType() - declaration.extensionReceiverParameter?.fixType() - declaration.valueParameters.forEach { it.fixType() } - if (declaration.returnType.isUnlinked()) { - linked = false - declaration.returnType = unlinkedMarkerTypeHandler.unlinkedMarkerType - } - declaration.typeParameters.forEach { - if (it.superTypes.any { s -> s.isUnlinked() }) { - linked = false - it.superTypes = listOf(unlinkedMarkerTypeHandler.unlinkedMarkerType) - } - } - if (linked) { - declaration.transformChildrenVoid() - } else { - declaration.reportWarning("Function", declaration.fqNameForIrSerialization.asString()) + val newFunction = declaration.replaceIfUnimplementedFakeOverride() + val removedUnlinkedTypes = newFunction.fixUnlinkedTypes() - declaration.body?.let { body -> - val bb = (body as IrBlockBody) - bb.statements.clear() - bb.statements.add(declaration.throwLinkageError(unlinkedSymbol = null, "Unlinked type in IR function signature")) - } - } - return declaration + val isImplementedFakeOverride = newFunction != declaration || declaration.isAccessorOfImplementedFakeOverrideProperty + + return newFunction.transformBodyIfNecessary(isImplementedFakeOverride, removedUnlinkedTypes) } override fun visitField(declaration: IrField): IrStatement { if (declaration.type.isUnlinked()) { - val fqn = declaration.correspondingPropertySymbol?.owner?.fqNameWhenAvailable ?: declaration.fqNameForIrSerialization val kind = if (declaration.correspondingPropertySymbol != null) "Property" else "Field" - declaration.reportWarning(kind, fqn.asString()) + declaration.reportUnlinkedSymbolsWarning(kind, fqn) declaration.type = unlinkedMarkerTypeHandler.unlinkedMarkerType declaration.initializer = null @@ -142,6 +125,98 @@ internal class UnlinkedDeclarationsProcessor( } return declaration } + + /** + * Replaces an [IrProperty] or [IrSimpleFunction] that is abstract fake override in non-abstract class + * by the corresponding non-abstract IR element. + */ + private fun T.replaceIfUnimplementedFakeOverride(): T { + if (this !is IrOverridableDeclaration<*> || !isFakeOverride || modality != Modality.ABSTRACT) return this + + val clazz = parentAsClass + if (clazz.modality == Modality.ABSTRACT || clazz.modality == Modality.SEALED) return this + + return deepCopyWithImplementedFakeOverrides() + } + + /** + * 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 functionKind = when { + this@transformBodyIfNecessary is IrSimpleFunction && correspondingPropertySymbol != null -> "property accessor" + this@transformBodyIfNecessary is IrConstructor -> "constructor" + else -> "function" + } + + val errorMessages = listOfNotNull( + if (isImplementedFakeOverride) + buildString { + append("Abstract ").append(functionKind).append(" ") + append(fqNameForIrSerialization.asString()) + append(" is not implemented in non-abstract class ") + append(parent.fqNameForIrSerialization.asString()) + } + else null, + if (removedUnlinkedTypes.isNotEmpty()) + buildString { + append("The signature of ").append(functionKind).append(" ") + append(fqNameForIrSerialization.asString()) + append(" contains unlinked symbols: ") + removedUnlinkedTypes.joinTo(this) { it.render() } + } + else null + ) + + errorMessages.forEach { reportWarning(it, location()) } + + body?.let { body -> + val bb = body as IrBlockBody + bb.statements.clear() + bb.statements.add(throwLinkageError(unlinkedSymbol = null, errorMessages.joinToString(separator = "; "))) + } + } + + return this + } } private fun IrSymbol.isUnlinked(): Boolean {