[FIR] Treat properties from ObjC-classes as abstract

In short, the problem is some
platform libraries contain invalid
Kotlin code. Specifically,
some classes may inherit multiple
members with default
implementations, but do not define
an explicit override, like `UIView.bounds`.

Since we can't refactor platrofm
libraries just now, we want to
treat such properties as abstract.

^KT-65866 Fixed
^KT-65855
This commit is contained in:
Nikolay Lunyak
2024-02-22 15:23:59 +02:00
committed by Space Team
parent 2ae8d28b33
commit 22fc90e5a1
4 changed files with 27 additions and 9 deletions
@@ -5,16 +5,20 @@
package org.jetbrains.kotlin.fir.backend.native
import org.jetbrains.kotlin.descriptors.Visibilities
import org.jetbrains.kotlin.descriptors.Visibility
import org.jetbrains.kotlin.fir.FirSession
import org.jetbrains.kotlin.fir.backend.native.interop.decodeObjCMethodAnnotation
import org.jetbrains.kotlin.fir.declarations.FirCallableDeclaration
import org.jetbrains.kotlin.fir.declarations.FirProperty
import org.jetbrains.kotlin.fir.declarations.FirSimpleFunction
import org.jetbrains.kotlin.fir.backend.native.interop.isObjCClass
import org.jetbrains.kotlin.fir.containingClassLookupTag
import org.jetbrains.kotlin.fir.declarations.*
import org.jetbrains.kotlin.fir.resolve.toSymbol
import org.jetbrains.kotlin.fir.scopes.FirOverrideChecker
import org.jetbrains.kotlin.fir.scopes.MemberWithBaseScope
import org.jetbrains.kotlin.fir.scopes.impl.FirStandardOverrideChecker
import org.jetbrains.kotlin.fir.scopes.impl.*
import org.jetbrains.kotlin.fir.symbols.impl.FirCallableSymbol
import org.jetbrains.kotlin.fir.symbols.impl.FirClassSymbol
import org.jetbrains.kotlin.fir.symbols.impl.FirPropertySymbol
import org.jetbrains.kotlin.fir.symbols.impl.FirRegularClassSymbol
/**
@@ -37,7 +41,14 @@ class FirNativeOverrideChecker(private val session: FirSession) : FirOverrideChe
override fun <D : FirCallableSymbol<*>> chooseIntersectionVisibility(
extractedOverrides: Collection<MemberWithBaseScope<D>>,
dispatchClassSymbol: FirRegularClassSymbol?,
): Visibility = standardOverrideChecker.chooseIntersectionVisibility(extractedOverrides, dispatchClassSymbol)
): Visibility {
val nonSubsumed = extractedOverrides.getNonSubsumedNonPhantomOverriddenSymbols()
return chooseIntersectionVisibilityOrNull(nonSubsumed) { it.isAbstract || it.member.isObjCClassProperty(session) }
?: Visibilities.Unknown
}
private fun FirCallableSymbol<*>.isObjCClassProperty(session: FirSession) =
this is FirPropertySymbol && (containingClassLookupTag()?.toSymbol(session) as? FirClassSymbol<*>)?.isObjCClass(session) ?: false
/**
* mimics ObjCOverridabilityCondition.isOverridable
@@ -109,7 +109,7 @@ internal fun FirConstructorSymbol.isObjCConstructor(session: FirSession) =
/**
* mimics IrClass.isObjCClass()
*/
private fun FirClassSymbol<*>.isObjCClass(session: FirSession) = classId.packageFqName != NativeStandardInteropNames.cInteropPackage &&
fun FirClassSymbol<*>.isObjCClass(session: FirSession) = classId.packageFqName != NativeStandardInteropNames.cInteropPackage &&
selfOrAnySuperClass(session) {
it.classId == NativeStandardInteropNames.objCObjectClassId
}
@@ -160,9 +160,15 @@ fun MemberWithBaseScope<FirCallableSymbol<*>>.getNonSubsumedOverriddenSymbols():
.map { it.member }
}
fun List<MemberWithBaseScope<FirCallableSymbol<*>>>.getNonSubsumedNonPhantomOverriddenSymbols(): List<MemberWithBaseScope<FirCallableSymbol<*>>> {
fun Collection<MemberWithBaseScope<FirCallableSymbol<*>>>.getNonSubsumedNonPhantomOverriddenSymbols(): List<MemberWithBaseScope<FirCallableSymbol<*>>> {
// It's crucial that we only unwrap phantom intersection overrides.
// See comments in the following tests for explanation:
// - intersectionWithMultipleDefaultsInJavaOverriddenByIntersectionInKotlin.kt
// - intersectionOverridesIntersection.kt
return flatMap { it.flattenPhantomIntersectionsRecursively() }
.nonSubsumed()
// To learn why `distinctBy` is needed, see:
// - intersectionWithMultipleDefaultsInJavaWithAdditionalSymbolsAfterNonSubsumed.kt
.distinctBy { it.member.unwrapSubstitutionOverrides<FirCallableSymbol<*>>() }
}
@@ -64,12 +64,13 @@ fun <D : FirCallableSymbol<*>> overrides(
return result
}
fun chooseIntersectionVisibilityOrNull(
inline fun chooseIntersectionVisibilityOrNull(
nonSubsumedOverrides: List<MemberWithBaseScope<FirCallableSymbol<*>>>,
isAbstract: (MemberWithBaseScope<FirCallableSymbol<*>>) -> Boolean = MemberWithBaseScope<FirCallableSymbol<*>>::isAbstract,
): Visibility? {
val nonAbstract = nonSubsumedOverrides.filter {
// Kotlin's Cloneable interface contains phantom `protected open fun clone()`.
!it.isAbstract && it.member.callableId != StandardClassIds.Callables.clone
!isAbstract(it) && it.member.callableId != StandardClassIds.Callables.clone
}
val allAreAbstract = nonAbstract.isEmpty()