[PL] Make call an exception in case of several inherited implementations

^KT-57701
This commit is contained in:
Pavel Kunyavskiy
2023-05-15 15:01:22 +02:00
committed by Space Team
parent fde8909e6f
commit ee1608ec99
22 changed files with 386 additions and 13 deletions
@@ -6,23 +6,22 @@
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.IrDeclarationOrigin
import org.jetbrains.kotlin.ir.declarations.IrField
import org.jetbrains.kotlin.ir.declarations.IrOverridableMember
import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.linkage.partial.IrUnimplementedOverridesStrategy
import org.jetbrains.kotlin.ir.linkage.partial.PartiallyLinkedDeclarationOrigin
import org.jetbrains.kotlin.ir.symbols.IrSymbol
import org.jetbrains.kotlin.ir.types.isNothing
import org.jetbrains.kotlin.ir.util.collectRealOverrides
import org.jetbrains.kotlin.ir.util.isInterface
internal class ImplementAsErrorThrowingStubs(
private val partialLinkageSupport: PartialLinkageSupportForLinker
) : IrUnimplementedOverridesStrategy {
override fun <T : IrOverridableMember> computeCustomization(overridableMember: T, parent: IrClass) =
override fun <S : IrSymbol, T : IrOverridableDeclaration<S>> computeCustomization(overridableMember: T, parent: IrClass) =
if (overridableMember.isAbstract
&& parent.isConcrete
&& !parent.isExternal
&& parent.isEligibleForPartialLinkage()
&& !parent.delegatesToNothing
&& !partialLinkageSupport.shouldBeSkipped(parent)
) {
IrUnimplementedOverridesStrategy.Customization(
origin = PartiallyLinkedDeclarationOrigin.UNIMPLEMENTED_ABSTRACT_CALLABLE_MEMBER,
@@ -30,6 +29,46 @@ internal class ImplementAsErrorThrowingStubs(
)
} else IrUnimplementedOverridesStrategy.Customization.NO
override fun <S : IrSymbol, T : IrOverridableDeclaration<S>> postProcessGeneratedFakeOverride(overridableMember: T, parent: IrClass) {
if (parent.isEligibleForPartialLinkage() && overridableMember.isAmbiguous()) {
fun IrOverridableDeclaration<*>.mark() {
if (isFakeOverride) {
origin = PartiallyLinkedDeclarationOrigin.AMBIGUOUS_NON_OVERRIDDEN_CALLABLE_MEMBER
isFakeOverride = false
}
}
overridableMember.mark()
if (overridableMember is IrProperty) {
overridableMember.getter?.mark()
overridableMember.setter?.mark()
}
}
}
/**
* The function returns if fake override has unique implementation in super classes to be chosen on call
*
* Candidates is a list of real functions we override, which are not overridden themselves
* by any other function in the list.
*
* If there is a **real** super-class function in the list, it must be unique.
* In that case it is preferred over functions coming from default implementation in interfaces.
*
* If there is no such function, but there are several interface function - it is an incompatible change.
*
* This is done to mimic jvm behaviour.
*/
private fun <S : IrSymbol, T : IrOverridableDeclaration<S>> T.isAmbiguous(): Boolean {
val candidates = collectRealOverrides().filter { !it.isAbstract }
if (candidates.any { ((it.symbol.owner as IrDeclaration).parent as? IrClass)?.isInterface == false }) {
return false
}
return candidates.size > 1
}
private fun IrClass.isEligibleForPartialLinkage() = !isExternal && !partialLinkageSupport.shouldBeSkipped(this)
private val IrOverridableMember.isAbstract: Boolean
get() = modality == Modality.ABSTRACT
@@ -79,6 +79,8 @@ internal fun PartialLinkageCase.renderLinkageError(): String = buildString {
is UnimplementedAbstractCallable -> unimplementedAbstractCallable(callable)
is UnusableAnnotation -> unusableAnnotation(annotationConstructorSymbol, holderDeclarationSymbol)
is AmbiguousNonOverriddenCallable -> ambiguousNonOverriddenCallable(callable)
}
}
@@ -588,6 +590,13 @@ private fun Appendable.unusableAnnotation(annotationConstructorSymbol: IrConstru
append("Unusable annotation ").declarationName(annotationConstructorSymbol)
.append(" has been removed from ").declarationKindName(holderDeclarationSymbol, capitalized = false)
private fun Appendable.ambiguousNonOverriddenCallable(callable: IrOverridableDeclaration<*>): Appendable =
declarationKindName(callable.symbol, capitalized = true)
.append(" in ")
.declarationKindName(callable.parentAsClass.symbol, capitalized = false)
.append(" inherits more than one default implementation")
private fun Appendable.appendCapitalized(text: String, capitalized: Boolean): Appendable {
if (capitalized && text.isNotEmpty()) {
val firstChar = text[0]
@@ -265,6 +265,7 @@ internal class PartiallyLinkedIrTreePatcher(
// Compute the linkage case.
val partialLinkageCase = when (declaration.origin) {
PartiallyLinkedDeclarationOrigin.UNIMPLEMENTED_ABSTRACT_CALLABLE_MEMBER -> UnimplementedAbstractCallable(declaration)
PartiallyLinkedDeclarationOrigin.AMBIGUOUS_NON_OVERRIDDEN_CALLABLE_MEMBER -> AmbiguousNonOverriddenCallable(declaration)
PartiallyLinkedDeclarationOrigin.MISSING_DECLARATION -> MissingDeclaration(declaration.symbol)
else -> unusableClassifierInSignature?.let { DeclarationWithUnusableClassifier(declaration.symbol, it) }
}