From 22fc90e5a185002a36f7cf935ccaee6038642800 Mon Sep 17 00:00:00 2001 From: Nikolay Lunyak Date: Thu, 22 Feb 2024 15:23:59 +0200 Subject: [PATCH] [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 --- .../native/FirNativeOverrideChecker.kt | 21 ++++++++++++++----- .../backend/native/interop/FirObjCInterop.kt | 2 +- .../fir/declarations/declarationUtils.kt | 8 ++++++- .../fir/scopes/impl/FirOverrideUtils.kt | 5 +++-- 4 files changed, 27 insertions(+), 9 deletions(-) diff --git a/compiler/fir/native/src/org/jetbrains/kotlin/fir/backend/native/FirNativeOverrideChecker.kt b/compiler/fir/native/src/org/jetbrains/kotlin/fir/backend/native/FirNativeOverrideChecker.kt index bf5a8a6e094..b80848d3a21 100644 --- a/compiler/fir/native/src/org/jetbrains/kotlin/fir/backend/native/FirNativeOverrideChecker.kt +++ b/compiler/fir/native/src/org/jetbrains/kotlin/fir/backend/native/FirNativeOverrideChecker.kt @@ -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 > chooseIntersectionVisibility( extractedOverrides: Collection>, 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 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 5872c028ec0..37403dc2a3d 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 @@ -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 } diff --git a/compiler/fir/providers/src/org/jetbrains/kotlin/fir/declarations/declarationUtils.kt b/compiler/fir/providers/src/org/jetbrains/kotlin/fir/declarations/declarationUtils.kt index 222a5afe9f4..dfa0b1e8211 100644 --- a/compiler/fir/providers/src/org/jetbrains/kotlin/fir/declarations/declarationUtils.kt +++ b/compiler/fir/providers/src/org/jetbrains/kotlin/fir/declarations/declarationUtils.kt @@ -160,9 +160,15 @@ fun MemberWithBaseScope>.getNonSubsumedOverriddenSymbols(): .map { it.member } } -fun List>>.getNonSubsumedNonPhantomOverriddenSymbols(): List>> { +fun Collection>>.getNonSubsumedNonPhantomOverriddenSymbols(): List>> { + // 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>() } } diff --git a/compiler/fir/providers/src/org/jetbrains/kotlin/fir/scopes/impl/FirOverrideUtils.kt b/compiler/fir/providers/src/org/jetbrains/kotlin/fir/scopes/impl/FirOverrideUtils.kt index 774b9cc801f..fbda9bf6fc7 100644 --- a/compiler/fir/providers/src/org/jetbrains/kotlin/fir/scopes/impl/FirOverrideUtils.kt +++ b/compiler/fir/providers/src/org/jetbrains/kotlin/fir/scopes/impl/FirOverrideUtils.kt @@ -64,12 +64,13 @@ fun > overrides( return result } -fun chooseIntersectionVisibilityOrNull( +inline fun chooseIntersectionVisibilityOrNull( nonSubsumedOverrides: List>>, + isAbstract: (MemberWithBaseScope>) -> Boolean = MemberWithBaseScope>::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()