[IR] Fix removal-of-abstract-fun IR linkage case

^KT-50771
This commit is contained in:
Dmitriy Dolovov
2022-04-27 20:15:55 +03:00
parent deb9039b0f
commit 8de4eb798c
3 changed files with 218 additions and 48 deletions
@@ -24,8 +24,13 @@ import org.jetbrains.kotlin.name.Name
inline fun <reified T : IrElement> T.deepCopyWithSymbols(
initialParent: IrDeclarationParent? = null,
createCopier: (SymbolRemapper, TypeRemapper) -> DeepCopyIrTreeWithSymbols = ::DeepCopyIrTreeWithSymbols
): T = deepCopyWithSymbols(initialParent, DeepCopySymbolRemapper(), createCopier)
inline fun <reified T : IrElement> 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
@@ -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 <reified T : IrOverridableDeclaration<*>> 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) {}
@@ -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<IrProperty>()
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 : IrDeclaration> 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<IrType> = buildSet {
fun IrValueParameter.fixType() {
if (type.isUnlinked()) {
this@buildSet += type
type = unlinkedMarkerTypeHandler.unlinkedMarkerType
defaultValue = null
}
varargElementType?.let {
if (it.isUnlinked()) {
this@buildSet += it
varargElementType = unlinkedMarkerTypeHandler.unlinkedMarkerType
}
}
}
dispatchReceiverParameter?.fixType()
extensionReceiverParameter?.fixType()
valueParameters.forEach { it.fixType() }
if (returnType.isUnlinked()) {
this += returnType
returnType = unlinkedMarkerTypeHandler.unlinkedMarkerType
}
typeParameters.forEach {
val unlinkedSuperType = it.superTypes.firstOrNull { s -> s.isUnlinked() }
if (unlinkedSuperType != null) {
this += unlinkedSuperType
it.superTypes = listOf(unlinkedMarkerTypeHandler.unlinkedMarkerType)
}
}
}
private fun IrFunction.transformBodyIfNecessary(
isImplementedFakeOverride: Boolean,
removedUnlinkedTypes: Set<IrType>
): IrFunction {
if (!isImplementedFakeOverride && removedUnlinkedTypes.isEmpty()) {
transformChildrenVoid()
} else {
val 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 {