[KMP Separate Compilation] Match by parameter names when actualizing ObjC methods

`platform.Foundation.NsCalendar` contains two `getEra` overloads with
the same signatures except parameter names. This is legal in ObjC, so
we support it.

To prevent an ambiguity during actualization of `NsCalendar`, we match
methods annotated by @ObjCMethod by parameter names in addition to all
the other criteria.

#KT-82418 Fixed
diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/mpp/FirExpectActualMatchingContextImpl.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/mpp/FirExpectActualMatchingContextImpl.kt
index cbccc39..7e5a6d2 100644
--- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/mpp/FirExpectActualMatchingContextImpl.kt
+++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/mpp/FirExpectActualMatchingContextImpl.kt
@@ -467,6 +467,9 @@
     override val CallableSymbolMarker.hasStableParameterNames: Boolean
         get() = asSymbol().rawStatus.hasStableParameterNames
 
+    override val CallableSymbolMarker.shouldMatchByParameterNames: Boolean
+        get() = false
+
     override val CallableSymbolMarker.isJavaField: Boolean
         get() = this is FirFieldSymbol && this.fir.unwrapFakeOverrides().isJava
 
diff --git a/compiler/ir/ir.actualization/src/main/kotlin/org/jetbrains/kotlin/backend/common/actualizer/IrExpectActualMatchingContext.kt b/compiler/ir/ir.actualization/src/main/kotlin/org/jetbrains/kotlin/backend/common/actualizer/IrExpectActualMatchingContext.kt
index 4330144..e07412f 100644
--- a/compiler/ir/ir.actualization/src/main/kotlin/org/jetbrains/kotlin/backend/common/actualizer/IrExpectActualMatchingContext.kt
+++ b/compiler/ir/ir.actualization/src/main/kotlin/org/jetbrains/kotlin/backend/common/actualizer/IrExpectActualMatchingContext.kt
@@ -23,6 +23,7 @@
 import org.jetbrains.kotlin.mpp.*
 import org.jetbrains.kotlin.name.CallableId
 import org.jetbrains.kotlin.name.ClassId
+import org.jetbrains.kotlin.name.FqName
 import org.jetbrains.kotlin.name.Name
 import org.jetbrains.kotlin.name.StandardClassIds
 import org.jetbrains.kotlin.resolve.calls.mpp.ExpectActualCollectionArgumentsCompatibilityCheckStrategy
@@ -502,20 +503,35 @@
 
     override val CallableSymbolMarker.hasStableParameterNames: Boolean
         get() {
-            var ir = asIr()
-
-            if (ir.isFakeOverride && ir is IrOverridableDeclaration<*>) {
-                ir.resolveFakeOverrideMaybeAbstract()?.let { ir = it }
-            }
-
+            val ir = asIrResolvingFakeOverrides()
             return when (ir.origin) {
                 IrDeclarationOrigin.IR_EXTERNAL_DECLARATION_STUB,
                 IrDeclarationOrigin.IR_EXTERNAL_JAVA_DECLARATION_STUB,
-                -> false
+                    -> false
                 else -> true
             }
         }
 
+    private fun CallableSymbolMarker.asIrResolvingFakeOverrides(): IrDeclaration {
+        var ir = asIr()
+        if (ir.isFakeOverride && ir is IrOverridableDeclaration<*>) {
+            ir.resolveFakeOverrideMaybeAbstract()?.let { ir = it }
+        }
+        return ir
+    }
+
+    private val objCAnnotations: List<ClassId> = run {
+        val packageFqName = FqName("kotlinx.cinterop")
+        listOf("ObjCMethod", "ObjCConstructor", "ObjCFactory")
+            .map { ClassId(packageFqName, Name.identifier(it)) }
+    }
+
+    override val CallableSymbolMarker.shouldMatchByParameterNames: Boolean
+        get() {
+            val ir = asIrResolvingFakeOverrides()
+            return ir.origin == IrDeclarationOrigin.IR_EXTERNAL_DECLARATION_STUB && objCAnnotations.any { ir.hasAnnotation(it) }
+        }
+
     override val CallableSymbolMarker.isJavaField: Boolean
         get() = this is IrPropertySymbol && owner.isPropertyForJavaField()
 
diff --git a/compiler/resolution.common/src/org/jetbrains/kotlin/resolve/calls/mpp/AbstractExpectActualChecker.kt b/compiler/resolution.common/src/org/jetbrains/kotlin/resolve/calls/mpp/AbstractExpectActualChecker.kt
index eb5e71c..f7e6ad4 100644
--- a/compiler/resolution.common/src/org/jetbrains/kotlin/resolve/calls/mpp/AbstractExpectActualChecker.kt
+++ b/compiler/resolution.common/src/org/jetbrains/kotlin/resolve/calls/mpp/AbstractExpectActualChecker.kt
@@ -656,7 +656,7 @@
 
     // ---------------------------------------- Utils ----------------------------------------
 
-    private inline fun <T, K> sizesAreEqualAndElementsNotEqualBy(first: List<T>, second: List<T>, selector: (T) -> K): Boolean {
+    internal inline fun <T, K> sizesAreEqualAndElementsNotEqualBy(first: List<T>, second: List<T>, selector: (T) -> K): Boolean {
         if (first.size != second.size) return false
         for (i in first.indices) {
             if (selector(first[i]) != selector(second[i])) return true
diff --git a/compiler/resolution.common/src/org/jetbrains/kotlin/resolve/calls/mpp/AbstractExpectActualMatcher.kt b/compiler/resolution.common/src/org/jetbrains/kotlin/resolve/calls/mpp/AbstractExpectActualMatcher.kt
index 4b5bbbb..21c91da 100644
--- a/compiler/resolution.common/src/org/jetbrains/kotlin/resolve/calls/mpp/AbstractExpectActualMatcher.kt
+++ b/compiler/resolution.common/src/org/jetbrains/kotlin/resolve/calls/mpp/AbstractExpectActualMatcher.kt
@@ -7,6 +7,7 @@
 
 import org.jetbrains.kotlin.descriptors.ClassKind
 import org.jetbrains.kotlin.mpp.*
+import org.jetbrains.kotlin.resolve.calls.mpp.AbstractExpectActualChecker.sizesAreEqualAndElementsNotEqualBy
 import org.jetbrains.kotlin.resolve.multiplatform.ExpectActualMatchingCompatibility
 import org.jetbrains.kotlin.types.model.KotlinTypeMarker
 import org.jetbrains.kotlin.types.model.TypeSubstitutorMarker
@@ -205,6 +206,14 @@
             return ExpectActualMatchingCompatibility.FunctionTypeParameterUpperBounds
         }
 
+        if (
+            actualDeclaration.shouldMatchByParameterNames &&
+            expectDeclaration.shouldMatchByParameterNames &&
+            sizesAreEqualAndElementsNotEqualBy(expectedValueParameters, actualValueParameters) { nameOf(it) }
+        ) {
+            return ExpectActualMatchingCompatibility.ParameterNames
+        }
+
         return ExpectActualMatchingCompatibility.MatchedSuccessfully
     }
 
diff --git a/compiler/resolution.common/src/org/jetbrains/kotlin/resolve/calls/mpp/ExpectActualMatchingContext.kt b/compiler/resolution.common/src/org/jetbrains/kotlin/resolve/calls/mpp/ExpectActualMatchingContext.kt
index 61d6acf..e0aa610 100644
--- a/compiler/resolution.common/src/org/jetbrains/kotlin/resolve/calls/mpp/ExpectActualMatchingContext.kt
+++ b/compiler/resolution.common/src/org/jetbrains/kotlin/resolve/calls/mpp/ExpectActualMatchingContext.kt
@@ -160,6 +160,8 @@
 
     val CallableSymbolMarker.hasStableParameterNames: Boolean
 
+    val CallableSymbolMarker.shouldMatchByParameterNames: Boolean
+
     val CallableSymbolMarker.isJavaField: Boolean
     val CallableSymbolMarker.canBeActualizedByJavaField: Boolean
 
diff --git a/core/compiler.common/src/org/jetbrains/kotlin/resolve/multiplatform/ExpectActualCompatibility.kt b/core/compiler.common/src/org/jetbrains/kotlin/resolve/multiplatform/ExpectActualCompatibility.kt
index 49e0477..4370fa0 100644
--- a/core/compiler.common/src/org/jetbrains/kotlin/resolve/multiplatform/ExpectActualCompatibility.kt
+++ b/core/compiler.common/src/org/jetbrains/kotlin/resolve/multiplatform/ExpectActualCompatibility.kt
@@ -26,6 +26,7 @@
     object ContextParameterCount : Mismatch("the number of context parameters is different")
     object FunctionTypeParameterCount : Mismatch(TYPE_PARAMETER_COUNT)
     object ParameterTypes : Mismatch("parameter types are different")
+    object ParameterNames : Mismatch("parameter names are different")
     object ContextParameterTypes : Mismatch("context parameter types are different")
     object FunctionTypeParameterUpperBounds : Mismatch(TYPE_PARAMETER_UPPER_BOUNDS)
     object MatchedSuccessfully : ExpectActualMatchingCompatibility()