in progress
diff --git a/compiler/fir/checkers/checkers-component-generator/src/org/jetbrains/kotlin/fir/checkers/generator/diagnostics/FirNativeDiagnosticsList.kt b/compiler/fir/checkers/checkers-component-generator/src/org/jetbrains/kotlin/fir/checkers/generator/diagnostics/FirNativeDiagnosticsList.kt
index 6ea06ab..48bf3a1 100644
--- a/compiler/fir/checkers/checkers-component-generator/src/org/jetbrains/kotlin/fir/checkers/generator/diagnostics/FirNativeDiagnosticsList.kt
+++ b/compiler/fir/checkers/checkers-component-generator/src/org/jetbrains/kotlin/fir/checkers/generator/diagnostics/FirNativeDiagnosticsList.kt
@@ -79,5 +79,8 @@
         val FORWARD_DECLARATION_AS_CLASS_LITERAL by error<KtElement> {
             parameter<ConeKotlinType>("type")
         }
+        val CONFLICTING_OBJC_OVERLOADS by error<PsiElement>(PositioningStrategy.DECLARATION_SIGNATURE_OR_DEFAULT) {
+            parameter<Collection<Symbol>>("conflictingOverloads")
+        }
     }
 }
diff --git a/compiler/fir/checkers/checkers.native/build.gradle.kts b/compiler/fir/checkers/checkers.native/build.gradle.kts
index 7009cfd..198911a 100644
--- a/compiler/fir/checkers/checkers.native/build.gradle.kts
+++ b/compiler/fir/checkers/checkers.native/build.gradle.kts
@@ -14,6 +14,7 @@
      */
     implementation(project(":compiler:frontend"))
     implementation(project(":compiler:psi"))
+    implementation(project(":compiler:fir:native"))
 
     compileOnly(project(":core:compiler.common.native"))
     compileOnly(commonDependency("org.jetbrains.kotlin:kotlin-reflect")) { isTransitive = false }
diff --git a/compiler/fir/checkers/checkers.native/gen/org/jetbrains/kotlin/fir/analysis/diagnostics/native/FirNativeErrors.kt b/compiler/fir/checkers/checkers.native/gen/org/jetbrains/kotlin/fir/analysis/diagnostics/native/FirNativeErrors.kt
index 0feac93..c57e566 100644
--- a/compiler/fir/checkers/checkers.native/gen/org/jetbrains/kotlin/fir/analysis/diagnostics/native/FirNativeErrors.kt
+++ b/compiler/fir/checkers/checkers.native/gen/org/jetbrains/kotlin/fir/analysis/diagnostics/native/FirNativeErrors.kt
@@ -52,6 +52,7 @@
     val UNCHECKED_CAST_TO_FORWARD_DECLARATION by warning2<KtElement, ConeKotlinType, ConeKotlinType>()
     val FORWARD_DECLARATION_AS_REIFIED_TYPE_ARGUMENT by error1<KtElement, ConeKotlinType>()
     val FORWARD_DECLARATION_AS_CLASS_LITERAL by error1<KtElement, ConeKotlinType>()
+    val CONFLICTING_OBJC_OVERLOADS by error1<PsiElement, Collection<FirBasedSymbol<*>>>(SourceElementPositioningStrategies.DECLARATION_SIGNATURE_OR_DEFAULT)
 
     init {
         RootDiagnosticRendererFactory.registerFactory(FirNativeErrorsDefaultMessages)
diff --git a/compiler/fir/checkers/checkers.native/src/org/jetbrains/kotlin/fir/analysis/diagnostics/native/FirNativeErrorsDefaultMessages.kt b/compiler/fir/checkers/checkers.native/src/org/jetbrains/kotlin/fir/analysis/diagnostics/native/FirNativeErrorsDefaultMessages.kt
index 7ba0837..52b2650 100644
--- a/compiler/fir/checkers/checkers.native/src/org/jetbrains/kotlin/fir/analysis/diagnostics/native/FirNativeErrorsDefaultMessages.kt
+++ b/compiler/fir/checkers/checkers.native/src/org/jetbrains/kotlin/fir/analysis/diagnostics/native/FirNativeErrorsDefaultMessages.kt
@@ -39,6 +39,7 @@
 import org.jetbrains.kotlin.fir.analysis.diagnostics.native.FirNativeErrors.SUBTYPE_OF_HIDDEN_FROM_OBJC
 import org.jetbrains.kotlin.fir.analysis.diagnostics.native.FirNativeErrors.THROWS_LIST_EMPTY
 import org.jetbrains.kotlin.fir.analysis.diagnostics.native.FirNativeErrors.UNCHECKED_CAST_TO_FORWARD_DECLARATION
+import org.jetbrains.kotlin.fir.analysis.diagnostics.native.FirNativeErrors.CONFLICTING_OBJC_OVERLOADS
 
 object FirNativeErrorsDefaultMessages : BaseDiagnosticRendererFactory() {
     override val MAP = KtDiagnosticFactoryToRendererMap("FIR").also { map ->
@@ -108,5 +109,10 @@
             "Can't refer to forward declaration ''{0}'' from class literal",
             FirDiagnosticRenderers.RENDER_TYPE
         )
+        map.put(
+            CONFLICTING_OBJC_OVERLOADS,
+            "Conflicting overloads: {0}. Add @ExperimentalObjCSignature to allow collision for functions inherited from objective-C.",
+            SYMBOLS
+        )
     }
 }
diff --git a/compiler/fir/checkers/checkers.native/src/org/jetbrains/kotlin/fir/analysis/native/checkers/FirNativeConflictsDeclarationChecker.kt b/compiler/fir/checkers/checkers.native/src/org/jetbrains/kotlin/fir/analysis/native/checkers/FirNativeConflictsDeclarationChecker.kt
new file mode 100644
index 0000000..d55b4d2
--- /dev/null
+++ b/compiler/fir/checkers/checkers.native/src/org/jetbrains/kotlin/fir/analysis/native/checkers/FirNativeConflictsDeclarationChecker.kt
@@ -0,0 +1,36 @@
+package org.jetbrains.kotlin.fir.analysis.native.checkers
+
+import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext
+import org.jetbrains.kotlin.fir.analysis.checkers.declaration.PlatformConflictDeclarationsDiagnosticDispatcher
+import org.jetbrains.kotlin.fir.analysis.diagnostics.native.FirNativeErrors
+import org.jetbrains.kotlin.fir.backend.native.interop.getObjCMethodInfoFromOverriddenFunctions
+import org.jetbrains.kotlin.fir.declarations.*
+import org.jetbrains.kotlin.fir.symbols.impl.FirFunctionSymbol
+import org.jetbrains.kotlin.name.ClassId
+import org.jetbrains.kotlin.name.Name
+import org.jetbrains.kotlin.name.NativeStandardInteropNames
+
+private val objCSignatureClassId = ClassId(NativeStandardInteropNames.cInteropPackage, Name.identifier("ExperimentalObjCSignature"))
+
+private fun FirFunctionSymbol<*>.isInheritedFromObjc(context: CheckerContext): Boolean {
+    return getObjCMethodInfoFromOverriddenFunctions(context.session, context.scopeSession) != null
+}
+
+private fun FirFunctionSymbol<*>.hasDifferentParameterNames(other: FirFunctionSymbol<*>) : Boolean {
+    return valueParameterSymbols.drop(1).map { it.name } != other.valueParameterSymbols.drop(1).map { it.name }
+}
+
+fun NativeConflictDeclarationsDiagnosticDispatcher() = PlatformConflictDeclarationsDiagnosticDispatcher dispatcher@{ declaration, symbols, context ->
+    if (declaration is FirFunction && symbols.all { it is FirFunctionSymbol<*> }) {
+        if (declaration.symbol.isInheritedFromObjc(context) && symbols.all { (it as FirFunctionSymbol<*>).isInheritedFromObjc(context) }) {
+            if (symbols.all { (it as FirFunctionSymbol<*>).hasDifferentParameterNames(declaration.symbol) }) {
+                if (declaration.hasAnnotation(objCSignatureClassId, context.session)) {
+                    return@dispatcher null
+                } else {
+                    return@dispatcher FirNativeErrors.CONFLICTING_OBJC_OVERLOADS
+                }
+            }
+        }
+    }
+    PlatformConflictDeclarationsDiagnosticDispatcher.DEFAULT.getDiagnostic(declaration, symbols, context)
+}
\ No newline at end of file
diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/declaration/FirConflictsDeclarationChecker.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/declaration/FirConflictsDeclarationChecker.kt
index d04f2f6..66767d9 100644
--- a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/declaration/FirConflictsDeclarationChecker.kt
+++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/declaration/FirConflictsDeclarationChecker.kt
@@ -7,8 +7,11 @@
 
 import org.jetbrains.kotlin.KtFakeSourceElementKind
 import org.jetbrains.kotlin.diagnostics.DiagnosticReporter
+import org.jetbrains.kotlin.diagnostics.KtDiagnosticFactory1
 import org.jetbrains.kotlin.diagnostics.reportOn
 import org.jetbrains.kotlin.fir.FirNameConflictsTrackerComponent
+import org.jetbrains.kotlin.fir.FirSession
+import org.jetbrains.kotlin.fir.FirSessionComponent
 import org.jetbrains.kotlin.fir.analysis.checkers.FirDeclarationInspector
 import org.jetbrains.kotlin.fir.analysis.checkers.checkForLocalRedeclarations
 import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext
@@ -23,6 +26,31 @@
 import org.jetbrains.kotlin.name.ClassId
 import org.jetbrains.kotlin.utils.SmartSet
 
+fun interface PlatformConflictDeclarationsDiagnosticDispatcher : FirSessionComponent {
+    fun getDiagnostic(
+        conflictingDeclaration: FirDeclaration,
+        symbols: SmartSet<FirBasedSymbol<*>>,
+        context: CheckerContext
+    ): KtDiagnosticFactory1<Collection<FirBasedSymbol<*>>>?
+
+    companion object {
+        val DEFAULT = PlatformConflictDeclarationsDiagnosticDispatcher { conflictingDeclaration, symbols, context ->
+            if (conflictingDeclaration is FirSimpleFunction || conflictingDeclaration is FirConstructor) {
+                FirErrors.CONFLICTING_OVERLOADS
+            } else if (conflictingDeclaration is FirClassLikeDeclaration &&
+                conflictingDeclaration.getContainingDeclaration(context.session) == null &&
+                symbols.any { it is FirClassLikeSymbol<*> }
+            ) {
+                FirErrors.PACKAGE_OR_CLASSIFIER_REDECLARATION
+            } else {
+                FirErrors.REDECLARATION
+            }
+        }
+    }
+}
+
+val FirSession.conflictDeclarationsDiagnosticDispatcher: PlatformConflictDeclarationsDiagnosticDispatcher? by FirSession.nullableSessionComponentAccessor()
+
 object FirConflictsDeclarationChecker : FirBasicDeclarationChecker() {
     override fun check(declaration: FirDeclaration, context: CheckerContext, reporter: DiagnosticReporter) {
         when (declaration) {
@@ -60,17 +88,8 @@
             val source = conflictingDeclaration.source
             if (symbols.isEmpty()) return@forEach
 
-            val factory =
-                if (conflictingDeclaration is FirSimpleFunction || conflictingDeclaration is FirConstructor) {
-                    FirErrors.CONFLICTING_OVERLOADS
-                } else if (conflictingDeclaration is FirClassLikeDeclaration &&
-                    conflictingDeclaration.getContainingDeclaration(context.session) == null &&
-                    symbols.any { it is FirClassLikeSymbol<*> }
-                ) {
-                    FirErrors.PACKAGE_OR_CLASSIFIER_REDECLARATION
-                } else {
-                    FirErrors.REDECLARATION
-                }
+            val factory = (context.session.conflictDeclarationsDiagnosticDispatcher ?: PlatformConflictDeclarationsDiagnosticDispatcher.DEFAULT)
+                .getDiagnostic(conflictingDeclaration, symbols, context) ?: return@forEach
 
             reporter.reportOn(source, factory, symbols, context)
         }
diff --git a/compiler/fir/entrypoint/src/org/jetbrains/kotlin/fir/session/FirNativeSessionFactory.kt b/compiler/fir/entrypoint/src/org/jetbrains/kotlin/fir/session/FirNativeSessionFactory.kt
index 80ab05f..66ef467 100644
--- a/compiler/fir/entrypoint/src/org/jetbrains/kotlin/fir/session/FirNativeSessionFactory.kt
+++ b/compiler/fir/entrypoint/src/org/jetbrains/kotlin/fir/session/FirNativeSessionFactory.kt
@@ -9,6 +9,8 @@
 import org.jetbrains.kotlin.fir.*
 import org.jetbrains.kotlin.fir.analysis.FirEmptyOverridesBackwardCompatibilityHelper
 import org.jetbrains.kotlin.fir.analysis.FirOverridesBackwardCompatibilityHelper
+import org.jetbrains.kotlin.fir.analysis.checkers.declaration.PlatformConflictDeclarationsDiagnosticDispatcher
+import org.jetbrains.kotlin.fir.analysis.native.checkers.NativeConflictDeclarationsDiagnosticDispatcher
 import org.jetbrains.kotlin.fir.checkers.registerNativeCheckers
 import org.jetbrains.kotlin.fir.deserialization.ModuleDataProvider
 import org.jetbrains.kotlin.fir.extensions.FirExtensionRegistrar
@@ -81,6 +83,7 @@
                 it.register(ConeCallConflictResolverFactory::class, DefaultCallConflictResolverFactory)
                 it.register(FirPlatformClassMapper::class, FirPlatformClassMapper.Default)
                 it.register(FirOverridesBackwardCompatibilityHelper::class, FirEmptyOverridesBackwardCompatibilityHelper)
+                it.register(PlatformConflictDeclarationsDiagnosticDispatcher::class, NativeConflictDeclarationsDiagnosticDispatcher())
                 registerExtraComponents(it)
             },
             registerExtraCheckers = { it.registerNativeCheckers() },
diff --git a/compiler/fir/native/src/org/jetbrains/kotlin/fir/backend/native/FirNativeKotlinMangler.kt b/compiler/fir/native/src/org/jetbrains/kotlin/fir/backend/native/FirNativeKotlinMangler.kt
index 76677e3..7b369e1 100644
--- a/compiler/fir/native/src/org/jetbrains/kotlin/fir/backend/native/FirNativeKotlinMangler.kt
+++ b/compiler/fir/native/src/org/jetbrains/kotlin/fir/backend/native/FirNativeKotlinMangler.kt
@@ -83,6 +83,7 @@
         val session = moduleData.session
         val scopeSession = ScopeSession()
         getInitMethodIfObjCConstructor(session, scopeSession)
+                ?.symbol
                 ?.getObjCMethodInfoFromOverriddenFunctions(session, scopeSession)
                 ?.let {
                     return buildString {
diff --git a/compiler/fir/native/src/org/jetbrains/kotlin/fir/backend/native/interop/FirObjCInterop.kt b/compiler/fir/native/src/org/jetbrains/kotlin/fir/backend/native/interop/FirObjCInterop.kt
index 412102f..5076226 100644
--- a/compiler/fir/native/src/org/jetbrains/kotlin/fir/backend/native/interop/FirObjCInterop.kt
+++ b/compiler/fir/native/src/org/jetbrains/kotlin/fir/backend/native/interop/FirObjCInterop.kt
@@ -24,6 +24,7 @@
 import org.jetbrains.kotlin.fir.symbols.SymbolInternals
 import org.jetbrains.kotlin.fir.symbols.impl.FirClassLikeSymbol
 import org.jetbrains.kotlin.fir.symbols.impl.FirClassSymbol
+import org.jetbrains.kotlin.fir.symbols.impl.FirFunctionSymbol
 import org.jetbrains.kotlin.fir.symbols.impl.FirNamedFunctionSymbol
 import org.jetbrains.kotlin.fir.types.classId
 import org.jetbrains.kotlin.name.Name
@@ -32,22 +33,21 @@
 import org.jetbrains.kotlin.utils.DFS
 
 
-@OptIn(SymbolInternals::class)
-internal fun FirFunction.getObjCMethodInfoFromOverriddenFunctions(session: FirSession, scopeSession: ScopeSession): ObjCMethodInfo? {
+fun FirFunctionSymbol<*>.getObjCMethodInfoFromOverriddenFunctions(session: FirSession, scopeSession: ScopeSession): ObjCMethodInfo? {
     decodeObjCMethodAnnotation(session)?.let {
         return it
     }
     // recursively find ObjCMethod annotation in getDirectOverriddenFunctions() (same as `overriddenDescriptors` in K1)
-    return when (val symbol = this.symbol) {
+    return when (this) {
         is FirNamedFunctionSymbol -> {
             val firClassSymbol = containingClassLookupTag()?.toSymbol(session) as FirClassSymbol<*>?
             firClassSymbol?.let {
                 val unsubstitutedScope = it.unsubstitutedScope(session, scopeSession, withForcedTypeCalculator = false, memberRequiredPhase = null)
                 // call of `processFunctionsByName()` is needed only for necessary side-effect before `getDirectOverriddenFunctions` call
-                unsubstitutedScope.processFunctionsByName(symbol.name) {}
-                unsubstitutedScope.getDirectOverriddenFunctions(symbol).firstNotNullOfOrNull {
-                    assert(it.fir != this) { "Function ${symbol.name}() is wrongly contained in its own getDirectOverriddenFunctions" }
-                    it.fir.getObjCMethodInfoFromOverriddenFunctions(session, scopeSession)
+                unsubstitutedScope.processFunctionsByName(name) {}
+                unsubstitutedScope.getDirectOverriddenFunctions(this).firstNotNullOfOrNull {
+                    assert(it != this) { "Function ${name}() is wrongly contained in its own getDirectOverriddenFunctions" }
+                    it.getObjCMethodInfoFromOverriddenFunctions(session, scopeSession)
                 }
             }
         }
@@ -78,7 +78,7 @@
 /**
  * mimics FunctionDescriptor.decodeObjCMethodAnnotation()
  */
-internal fun FirFunction.decodeObjCMethodAnnotation(session: FirSession): ObjCMethodInfo? =
+internal fun FirFunctionSymbol<*>.decodeObjCMethodAnnotation(session: FirSession): ObjCMethodInfo? =
         annotations.getAnnotationByClassId(NativeStandardInteropNames.objCMethodClassId, session)?.let {
             ObjCMethodInfo(
                     selector = it.constStringArgument("selector"),
@@ -87,6 +87,9 @@
                     directSymbol = annotations.getAnnotationByClassId(NativeStandardInteropNames.objCDirectClassId, session)?.constStringArgument("symbol"),
             )
         }
+internal fun FirFunction.decodeObjCMethodAnnotation(session: FirSession): ObjCMethodInfo? =
+        symbol.decodeObjCMethodAnnotation(session)
+
 
 
 private fun FirAnnotation.constStringArgument(argumentName: String): String =
diff --git a/kotlin-native/Interop/Runtime/src/main/kotlin/kotlinx/cinterop/Annotations.kt b/kotlin-native/Interop/Runtime/src/main/kotlin/kotlinx/cinterop/Annotations.kt
index 13a5f25..6f7b6a9 100644
--- a/kotlin-native/Interop/Runtime/src/main/kotlin/kotlinx/cinterop/Annotations.kt
+++ b/kotlin-native/Interop/Runtime/src/main/kotlin/kotlinx/cinterop/Annotations.kt
@@ -63,3 +63,8 @@
 @Retention(AnnotationRetention.BINARY)
 @RequiresOptIn(level = RequiresOptIn.Level.ERROR)
 public annotation class ExperimentalForeignApi
+
+
+@Target(AnnotationTarget.FUNCTION)
+@Retention(AnnotationRetention.BINARY)
+public annotation class ExperimentalObjCSignature