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)) }
}