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