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 index 9d199c1dc8c..86de59223b7 100644 --- 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 @@ -29,6 +29,7 @@ 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.isSubtypeOfClass import org.jetbrains.kotlin.ir.util.* import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid import org.jetbrains.kotlin.ir.visitors.IrElementVisitorVoid @@ -716,7 +717,28 @@ internal class PartiallyLinkedIrTreePatcher( val actualDispatchReceiverClassifier: IrClassifierSymbol? = (referenceType.arguments.firstOrNull() as? IrSimpleType)?.classifier - expectedDispatchReceiverClassifier == actualDispatchReceiverClassifier + /* + * FIR generates function references for certain overridden functions in a different way than K1. Example: + * class A + * + * fun test(a: A): Boolean { + * return (A::equals)(a, a) + * // ^^^ IrFunctionReferenceImpl slightly differs: + * // | Frontend | Attribute | Value | + * // +----------+--------------------------+--------------------------------+ + * // | K1 | dispatchReceiver | null | + * // | K1 | symbol.parent as IrClass | "class A" | + * // | K1 | type as IrSimpleType | "KFunction2" | + * // | FIR | dispatchReceiver | null | + * // | FIR | symbol.parent as IrClass | "class Any" | + * // | FIR | type as IrSimpleType | "KFunction2" | + * } + * + * So instead of checking that `expectedDispatchReceiverClassifier == actualDispatchReceiverClassifier` it's + * safer to check that `actualDispatchReceiverClassifier` is the same or subclass of `expectedDispatchReceiverClassifier`. + */ + // expectedDispatchReceiverClassifier == actualDispatchReceiverClassifier + actualDispatchReceiverClassifier?.isSubtypeOfClass(expectedDispatchReceiverClassifier) ?: false } else -> false } diff --git a/compiler/testData/klibABI/referencingUnusableDeclarations/lib2/l2.kt b/compiler/testData/klibABI/referencingUnusableDeclarations/lib2/l2.kt index 2281f1dcc8b..96c7c2f1b89 100644 --- a/compiler/testData/klibABI/referencingUnusableDeclarations/lib2/l2.kt +++ b/compiler/testData/klibABI/referencingUnusableDeclarations/lib2/l2.kt @@ -72,3 +72,19 @@ fun referenceFunctionWithRemovedTypeParameter(): String { */ return listOf(null).map(::functionWithRemovedTypeParameter).joinToString() } + +abstract class StableAbstractFunctionsHolder { + abstract fun foo(): String + open fun bar(): String = "bar" +} +class StableFunctionsHolder: StableAbstractFunctionsHolder() { + override fun foo(): String = "foo" + fun baz(): String = "baz" +} +data class StableClassWithEquals(val value: Int) + +fun referencingMemberFunctionFoo(sfh: StableFunctionsHolder): String = run(sfh::foo) +fun referencingMemberFunctionBar(sfh: StableFunctionsHolder): String = run(sfh::bar) +fun referencingMemberFunctionBaz(sfh: StableFunctionsHolder): String = run(sfh::baz) +fun referencingAnyEquals(any: Any): String = if ((Any::equals)(any, any)) "OK" else "FAIL" +fun referencingStableClassWithEquals(stwe: StableClassWithEquals): String = if ((StableClassWithEquals::equals)(stwe, stwe)) "OK" else "FAIL" diff --git a/compiler/testData/klibABI/referencingUnusableDeclarations/main/m.kt b/compiler/testData/klibABI/referencingUnusableDeclarations/main/m.kt index 9f4d8940913..0e971fc55d8 100644 --- a/compiler/testData/klibABI/referencingUnusableDeclarations/main/m.kt +++ b/compiler/testData/klibABI/referencingUnusableDeclarations/main/m.kt @@ -3,6 +3,7 @@ import abitestutils.abiTest fun box() = abiTest { val stableClass = StableClass() val stableClassInner = stableClass.Inner() + val sfh = StableFunctionsHolder() expectFailure(linkage("Reference to class 'RemovedClass' can not be evaluated: No class found for symbol '/RemovedClass'")) { referenceRemovedClassReference() } expectFailure(linkage("Reference to constructor 'RemovedClass.' can not be evaluated: No constructor found for symbol '/RemovedClass.'")) { referenceRemovedClassConstructorReference() } @@ -46,4 +47,10 @@ fun box() = abiTest { expectFailure(linkage("Reference to function 'functionWithUnlinkedParameter' can not be evaluated: Function uses unlinked class symbol '/RemovedClass'")) { referenceFunctionWithUnlinkedParameter() } expectFailure(linkage("Reference to function 'functionWithUnlinkedReturnValue' can not be evaluated: Function uses unlinked class symbol '/RemovedClass'")) { referenceFunctionWithUnlinkedReturnValue() } expectFailure(linkage("Reference to function 'functionWithRemovedTypeParameter' can not be evaluated: Function uses unlinked class symbol '/RemovedClass' (via type parameter 'T')")) { referenceFunctionWithRemovedTypeParameter() } + + expectSuccess("foo") { referencingMemberFunctionFoo(sfh) } + expectSuccess("bar") { referencingMemberFunctionBar(sfh) } + expectSuccess("baz") { referencingMemberFunctionBaz(sfh) } + expectSuccess { referencingAnyEquals(Any()) } + expectSuccess { referencingStableClassWithEquals(StableClassWithEquals(42)) } }