[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 package org.jetbrains.kotlin.fir.backend.native
import org.jetbrains.kotlin.descriptors.Visibilities
import org.jetbrains.kotlin.descriptors.Visibility import org.jetbrains.kotlin.descriptors.Visibility
import org.jetbrains.kotlin.fir.FirSession import org.jetbrains.kotlin.fir.FirSession
import org.jetbrains.kotlin.fir.backend.native.interop.decodeObjCMethodAnnotation import org.jetbrains.kotlin.fir.backend.native.interop.decodeObjCMethodAnnotation
import org.jetbrains.kotlin.fir.declarations.FirCallableDeclaration import org.jetbrains.kotlin.fir.backend.native.interop.isObjCClass
import org.jetbrains.kotlin.fir.declarations.FirProperty import org.jetbrains.kotlin.fir.containingClassLookupTag
import org.jetbrains.kotlin.fir.declarations.FirSimpleFunction 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.FirOverrideChecker
import org.jetbrains.kotlin.fir.scopes.MemberWithBaseScope 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.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 import org.jetbrains.kotlin.fir.symbols.impl.FirRegularClassSymbol
/** /**
@@ -37,7 +41,14 @@ class FirNativeOverrideChecker(private val session: FirSession) : FirOverrideChe
override fun <D : FirCallableSymbol<*>> chooseIntersectionVisibility( override fun <D : FirCallableSymbol<*>> chooseIntersectionVisibility(
extractedOverrides: Collection<MemberWithBaseScope<D>>, extractedOverrides: Collection<MemberWithBaseScope<D>>,
dispatchClassSymbol: FirRegularClassSymbol?, 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 * mimics ObjCOverridabilityCondition.isOverridable
@@ -109,7 +109,7 @@ internal fun FirConstructorSymbol.isObjCConstructor(session: FirSession) =
/** /**
* mimics IrClass.isObjCClass() * mimics IrClass.isObjCClass()
*/ */
private fun FirClassSymbol<*>.isObjCClass(session: FirSession) = classId.packageFqName != NativeStandardInteropNames.cInteropPackage && fun FirClassSymbol<*>.isObjCClass(session: FirSession) = classId.packageFqName != NativeStandardInteropNames.cInteropPackage &&
selfOrAnySuperClass(session) { selfOrAnySuperClass(session) {
it.classId == NativeStandardInteropNames.objCObjectClassId it.classId == NativeStandardInteropNames.objCObjectClassId
} }
@@ -160,9 +160,15 @@ fun MemberWithBaseScope<FirCallableSymbol<*>>.getNonSubsumedOverriddenSymbols():
.map { it.member } .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() } return flatMap { it.flattenPhantomIntersectionsRecursively() }
.nonSubsumed() .nonSubsumed()
// To learn why `distinctBy` is needed, see:
// - intersectionWithMultipleDefaultsInJavaWithAdditionalSymbolsAfterNonSubsumed.kt
.distinctBy { it.member.unwrapSubstitutionOverrides<FirCallableSymbol<*>>() } .distinctBy { it.member.unwrapSubstitutionOverrides<FirCallableSymbol<*>>() }
} }
@@ -64,12 +64,13 @@ fun <D : FirCallableSymbol<*>> overrides(
return result return result
} }
fun chooseIntersectionVisibilityOrNull( inline fun chooseIntersectionVisibilityOrNull(
nonSubsumedOverrides: List<MemberWithBaseScope<FirCallableSymbol<*>>>, nonSubsumedOverrides: List<MemberWithBaseScope<FirCallableSymbol<*>>>,
isAbstract: (MemberWithBaseScope<FirCallableSymbol<*>>) -> Boolean = MemberWithBaseScope<FirCallableSymbol<*>>::isAbstract,
): Visibility? { ): Visibility? {
val nonAbstract = nonSubsumedOverrides.filter { val nonAbstract = nonSubsumedOverrides.filter {
// Kotlin's Cloneable interface contains phantom `protected open fun clone()`. // 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() val allAreAbstract = nonAbstract.isEmpty()