[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()