[FIR] Relax intersection overrides visibility requirements for Java
Allow multiple bases with default implementations as long as there's a non-abstract symbol from a class. Our rules for Kotlin are stricter than those in Java.
This commit is contained in:
committed by
Space Team
parent
76ed5453b3
commit
da6006b7d0
+11
@@ -5,10 +5,13 @@
|
||||
|
||||
package org.jetbrains.kotlin.fir.scopes
|
||||
|
||||
import org.jetbrains.kotlin.descriptors.Visibility
|
||||
import org.jetbrains.kotlin.fir.FirSession
|
||||
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.symbols.impl.FirCallableSymbol
|
||||
import org.jetbrains.kotlin.fir.symbols.impl.FirRegularClassSymbol
|
||||
|
||||
/**
|
||||
* That class is expected to work just the same as FirStandardOverrideChecker for regular members,
|
||||
@@ -31,4 +34,12 @@ class FirIntersectionScopeOverrideChecker(session: FirSession) : FirOverrideChec
|
||||
platformSpecificOverridabilityRules?.isOverriddenProperty(overrideCandidate, baseDeclaration)?.let { return it }
|
||||
return standardOverrideChecker.isOverriddenProperty(overrideCandidate, baseDeclaration)
|
||||
}
|
||||
|
||||
override fun <D : FirCallableSymbol<*>> chooseIntersectionVisibility(
|
||||
extractedOverrides: Collection<MemberWithBaseScope<D>>,
|
||||
dispatchClassSymbol: FirRegularClassSymbol?,
|
||||
): Visibility {
|
||||
platformSpecificOverridabilityRules?.chooseIntersectionVisibility(extractedOverrides, dispatchClassSymbol)?.let { return it }
|
||||
return standardOverrideChecker.chooseIntersectionVisibility(extractedOverrides, dispatchClassSymbol)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
|
||||
package org.jetbrains.kotlin.fir.scopes
|
||||
|
||||
import org.jetbrains.kotlin.descriptors.Visibility
|
||||
import org.jetbrains.kotlin.fir.FirSession
|
||||
import org.jetbrains.kotlin.fir.FirSessionComponent
|
||||
import org.jetbrains.kotlin.fir.declarations.FirCallableDeclaration
|
||||
@@ -13,6 +14,7 @@ import org.jetbrains.kotlin.fir.declarations.FirSimpleFunction
|
||||
import org.jetbrains.kotlin.fir.symbols.impl.FirCallableSymbol
|
||||
import org.jetbrains.kotlin.fir.symbols.impl.FirNamedFunctionSymbol
|
||||
import org.jetbrains.kotlin.fir.symbols.impl.FirPropertySymbol
|
||||
import org.jetbrains.kotlin.fir.symbols.impl.FirRegularClassSymbol
|
||||
|
||||
interface FirOverrideChecker : FirSessionComponent {
|
||||
fun isOverriddenFunction(
|
||||
@@ -24,6 +26,11 @@ interface FirOverrideChecker : FirSessionComponent {
|
||||
overrideCandidate: FirCallableDeclaration, // NB: in Java it can be a function which overrides accessor
|
||||
baseDeclaration: FirProperty
|
||||
): Boolean
|
||||
|
||||
fun <D : FirCallableSymbol<*>> chooseIntersectionVisibility(
|
||||
extractedOverrides: Collection<MemberWithBaseScope<D>>,
|
||||
dispatchClassSymbol: FirRegularClassSymbol?,
|
||||
): Visibility
|
||||
}
|
||||
|
||||
fun FirOverrideChecker.isOverriddenFunction(
|
||||
|
||||
+8
@@ -5,11 +5,14 @@
|
||||
|
||||
package org.jetbrains.kotlin.fir.scopes
|
||||
|
||||
import org.jetbrains.kotlin.descriptors.Visibility
|
||||
import org.jetbrains.kotlin.fir.FirSession
|
||||
import org.jetbrains.kotlin.fir.FirSessionComponent
|
||||
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.symbols.impl.FirCallableSymbol
|
||||
import org.jetbrains.kotlin.fir.symbols.impl.FirRegularClassSymbol
|
||||
|
||||
interface PlatformSpecificOverridabilityRules : FirSessionComponent {
|
||||
// Thus functions return "null" in case the status should be defined via standard platform-independent rules
|
||||
@@ -22,6 +25,11 @@ interface PlatformSpecificOverridabilityRules : FirSessionComponent {
|
||||
overrideCandidate: FirCallableDeclaration,
|
||||
baseDeclaration: FirProperty
|
||||
): Boolean?
|
||||
|
||||
fun <D : FirCallableSymbol<*>> chooseIntersectionVisibility(
|
||||
extractedOverrides: Collection<MemberWithBaseScope<D>>,
|
||||
dispatchClassSymbol: FirRegularClassSymbol?,
|
||||
): Visibility?
|
||||
}
|
||||
|
||||
val FirSession.platformSpecificOverridabilityRules: PlatformSpecificOverridabilityRules? by FirSession.nullableSessionComponentAccessor()
|
||||
|
||||
@@ -5,10 +5,18 @@
|
||||
|
||||
package org.jetbrains.kotlin.fir.scopes.impl
|
||||
|
||||
import org.jetbrains.kotlin.descriptors.Modality
|
||||
import org.jetbrains.kotlin.descriptors.Visibilities
|
||||
import org.jetbrains.kotlin.descriptors.Visibility
|
||||
import org.jetbrains.kotlin.fir.declarations.FirMemberDeclaration
|
||||
import org.jetbrains.kotlin.fir.declarations.FirResolvedDeclarationStatus
|
||||
import org.jetbrains.kotlin.fir.declarations.utils.visibility
|
||||
import org.jetbrains.kotlin.fir.scopes.*
|
||||
import org.jetbrains.kotlin.fir.symbols.impl.FirCallableSymbol
|
||||
import org.jetbrains.kotlin.fir.symbols.impl.FirNamedFunctionSymbol
|
||||
import org.jetbrains.kotlin.fir.symbols.impl.FirPropertySymbol
|
||||
import org.jetbrains.kotlin.fir.unwrapSubstitutionOverrides
|
||||
import org.jetbrains.kotlin.name.StandardClassIds
|
||||
|
||||
fun filterOutOverriddenFunctions(extractedOverridden: Collection<MemberWithBaseScope<FirNamedFunctionSymbol>>): Collection<MemberWithBaseScope<FirNamedFunctionSymbol>> {
|
||||
return filterOutOverridden(extractedOverridden, FirTypeScope::processDirectOverriddenFunctionsWithBaseScope)
|
||||
@@ -55,3 +63,52 @@ fun <D : FirCallableSymbol<*>> overrides(
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
fun chooseIntersectionVisibilityOrNull(
|
||||
nonSubsumedOverrides: List<MemberWithBaseScope<FirCallableSymbol<*>>>,
|
||||
): Visibility? {
|
||||
val nonAbstract = nonSubsumedOverrides.filter {
|
||||
// Kotlin's Cloneable interface contains phantom `protected open fun clone()`.
|
||||
!it.isAbstract && it.member.callableId != StandardClassIds.Callables.clone
|
||||
}
|
||||
val allAreAbstract = nonAbstract.isEmpty()
|
||||
|
||||
if (allAreAbstract) {
|
||||
return findMaxVisibilityOrNull(nonSubsumedOverrides)
|
||||
}
|
||||
|
||||
return nonAbstract.singleOrNull()?.member?.rawStatus?.visibility
|
||||
}
|
||||
|
||||
val MemberWithBaseScope<FirCallableSymbol<*>>.isAbstract: Boolean
|
||||
get() {
|
||||
// This function is expected to be called during FirResolvePhase.STATUS,
|
||||
// meaning we can't yet access `resolvedStatus`, because it would require
|
||||
// the same phase, but by this time we expect the statuses to have been
|
||||
// calculated de-facto.
|
||||
require(member.rawStatus is FirResolvedDeclarationStatus)
|
||||
// Kotlin's Cloneable interface contains phantom `protected open fun clone()`.
|
||||
return member.rawStatus.modality == Modality.ABSTRACT
|
||||
}
|
||||
|
||||
fun <D : FirCallableSymbol<*>> List<MemberWithBaseScope<D>>.filterOutDuplicates(): List<MemberWithBaseScope<D>> {
|
||||
val uniqueSymbols = mutableSetOf<FirCallableSymbol<*>>()
|
||||
return filter { uniqueSymbols.add(it.member.fir.unwrapSubstitutionOverrides().symbol) }
|
||||
}
|
||||
|
||||
fun <D : FirCallableSymbol<*>> findMaxVisibilityOrNull(
|
||||
extractedOverrides: Collection<MemberWithBaseScope<D>>
|
||||
): Visibility? {
|
||||
var maxVisibility: Visibility = Visibilities.Private
|
||||
|
||||
for ((override) in extractedOverrides) {
|
||||
val visibility = (override.fir as FirMemberDeclaration).visibility
|
||||
val compare = Visibilities.compare(visibility, maxVisibility) ?: return null
|
||||
|
||||
if (compare > 0) {
|
||||
maxVisibility = visibility
|
||||
}
|
||||
}
|
||||
|
||||
return maxVisibility
|
||||
}
|
||||
|
||||
+13
@@ -6,12 +6,16 @@
|
||||
package org.jetbrains.kotlin.fir.scopes.impl
|
||||
|
||||
import org.jetbrains.kotlin.descriptors.Visibilities
|
||||
import org.jetbrains.kotlin.descriptors.Visibility
|
||||
import org.jetbrains.kotlin.fir.FirSession
|
||||
import org.jetbrains.kotlin.fir.declarations.*
|
||||
import org.jetbrains.kotlin.fir.declarations.utils.isSuspend
|
||||
import org.jetbrains.kotlin.fir.declarations.utils.visibility
|
||||
import org.jetbrains.kotlin.fir.resolve.substitution.ConeSubstitutor
|
||||
import org.jetbrains.kotlin.fir.resolve.transformers.ensureResolvedTypeDeclaration
|
||||
import org.jetbrains.kotlin.fir.scopes.MemberWithBaseScope
|
||||
import org.jetbrains.kotlin.fir.symbols.impl.FirCallableSymbol
|
||||
import org.jetbrains.kotlin.fir.symbols.impl.FirRegularClassSymbol
|
||||
import org.jetbrains.kotlin.fir.symbols.lazyResolveToPhase
|
||||
import org.jetbrains.kotlin.fir.types.*
|
||||
import org.jetbrains.kotlin.types.AbstractTypeChecker
|
||||
@@ -157,4 +161,13 @@ class FirStandardOverrideChecker(private val session: FirSession) : FirAbstractO
|
||||
isEqualTypes(memberParam.typeRef, selfParam.typeRef, substitutor)
|
||||
}
|
||||
}
|
||||
|
||||
override fun <D : FirCallableSymbol<*>> chooseIntersectionVisibility(
|
||||
extractedOverrides: Collection<MemberWithBaseScope<D>>,
|
||||
dispatchClassSymbol: FirRegularClassSymbol?,
|
||||
): Visibility {
|
||||
val overridesWithoutIntersections = extractedOverrides.flatMap { it.flattenIntersectionsRecursively() }
|
||||
val nonSubsumed = overridesWithoutIntersections.nonSubsumed().filterOutDuplicates()
|
||||
return chooseIntersectionVisibilityOrNull(nonSubsumed) ?: Visibilities.Unknown
|
||||
}
|
||||
}
|
||||
|
||||
+1
-54
@@ -13,7 +13,6 @@ import org.jetbrains.kotlin.fir.caches.*
|
||||
import org.jetbrains.kotlin.fir.declarations.*
|
||||
import org.jetbrains.kotlin.fir.declarations.utils.isExpect
|
||||
import org.jetbrains.kotlin.fir.declarations.utils.modality
|
||||
import org.jetbrains.kotlin.fir.declarations.utils.visibility
|
||||
import org.jetbrains.kotlin.fir.resolve.substitution.ConeSubstitutor
|
||||
import org.jetbrains.kotlin.fir.resolve.transformers.ReturnTypeCalculatorForFullBodyResolve
|
||||
import org.jetbrains.kotlin.fir.scopes.*
|
||||
@@ -22,7 +21,6 @@ import org.jetbrains.kotlin.fir.symbols.impl.*
|
||||
import org.jetbrains.kotlin.fir.types.*
|
||||
import org.jetbrains.kotlin.name.CallableId
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
import org.jetbrains.kotlin.name.StandardClassIds
|
||||
import org.jetbrains.kotlin.utils.addToStdlib.runIf
|
||||
import kotlin.contracts.ExperimentalContracts
|
||||
import kotlin.contracts.contract
|
||||
@@ -207,7 +205,7 @@ class FirTypeIntersectionScopeContext(
|
||||
extractedOverrides: List<MemberWithBaseScope<D>>,
|
||||
): MemberWithBaseScope<FirCallableSymbol<*>> {
|
||||
val newModality = chooseIntersectionOverrideModality(extractedOverrides.flatMap { it.flattenIntersectionsRecursively() }.nonSubsumed())
|
||||
val newVisibility = chooseIntersectionVisibility(extractedOverrides)
|
||||
val newVisibility = overrideChecker.chooseIntersectionVisibility(extractedOverrides, dispatchClassSymbol)
|
||||
val mostSpecificSymbols = mostSpecific.map { it.member }
|
||||
val extractedOverridesSymbols = extractedOverrides.map { it.member }
|
||||
val key = mostSpecific.first()
|
||||
@@ -320,57 +318,6 @@ class FirTypeIntersectionScopeContext(
|
||||
}
|
||||
}
|
||||
|
||||
private fun <D : FirCallableSymbol<*>> chooseIntersectionVisibility(
|
||||
extractedOverrides: Collection<MemberWithBaseScope<D>>
|
||||
): Visibility = chooseIntersectionVisibilityOrNull(extractedOverrides) ?: Visibilities.Unknown
|
||||
|
||||
private fun <D : FirCallableSymbol<*>> chooseIntersectionVisibilityOrNull(
|
||||
extractedOverrides: Collection<MemberWithBaseScope<D>>
|
||||
): Visibility? {
|
||||
val overridesWithoutIntersections = extractedOverrides.flatMap { it.flattenIntersectionsRecursively() }
|
||||
val nonSubsumed = overridesWithoutIntersections.nonSubsumed().filterOutDuplicates()
|
||||
val nonAbstract = nonSubsumed.filter {
|
||||
require(it.member.rawStatus is FirResolvedDeclarationStatus) {
|
||||
"We expect that to be true already, but we can't yet call resolvedStatus"
|
||||
}
|
||||
// Kotlin's Cloneable interface contains phantom `protected open fun clone()`.
|
||||
it.member.rawStatus.modality != Modality.ABSTRACT && it.member.callableId != StandardClassIds.Callables.clone
|
||||
}
|
||||
val allAreAbstract = nonAbstract.isEmpty()
|
||||
|
||||
if (allAreAbstract) {
|
||||
return findMaxVisibilityOrNull(nonSubsumed)
|
||||
}
|
||||
|
||||
if (nonAbstract.size >= 2) {
|
||||
return null
|
||||
}
|
||||
|
||||
return nonAbstract.single().member.rawStatus.visibility
|
||||
}
|
||||
|
||||
private fun <D : FirCallableSymbol<*>> List<MemberWithBaseScope<D>>.filterOutDuplicates(): List<MemberWithBaseScope<D>> {
|
||||
val uniqueSymbols = mutableSetOf<FirCallableSymbol<*>>()
|
||||
return filter { uniqueSymbols.add(it.member.fir.unwrapSubstitutionOverrides().symbol) }
|
||||
}
|
||||
|
||||
private fun <D : FirCallableSymbol<*>> findMaxVisibilityOrNull(
|
||||
extractedOverrides: Collection<MemberWithBaseScope<D>>
|
||||
): Visibility? {
|
||||
var maxVisibility: Visibility = Visibilities.Private
|
||||
|
||||
for ((override) in extractedOverrides) {
|
||||
val visibility = (override.fir as FirMemberDeclaration).visibility
|
||||
val compare = Visibilities.compare(visibility, maxVisibility) ?: return null
|
||||
|
||||
if (compare > 0) {
|
||||
maxVisibility = visibility
|
||||
}
|
||||
}
|
||||
|
||||
return maxVisibility
|
||||
}
|
||||
|
||||
private fun createIntersectionOverrideFunction(
|
||||
mostSpecific: Collection<FirCallableSymbol<*>>,
|
||||
overrides: Collection<FirCallableSymbol<*>>,
|
||||
|
||||
Reference in New Issue
Block a user