[FIR] Extract logic of creating intersection overrides into separate class

This commit is contained in:
Dmitriy Novozhilov
2021-12-08 12:35:29 +03:00
parent 3db341213b
commit 95a95450bf
3 changed files with 553 additions and 508 deletions
@@ -21,31 +21,6 @@ abstract class AbstractFirOverrideScope(
//base symbol as key, overridden as value
val overrideByBase = mutableMapOf<FirCallableSymbol<*>, FirCallableSymbol<*>?>()
private fun isOverriddenFunction(overrideCandidate: FirSimpleFunction, baseDeclaration: FirSimpleFunction): Boolean {
return overrideChecker.isOverriddenFunction(overrideCandidate, baseDeclaration)
}
private fun isOverriddenProperty(overrideCandidate: FirCallableDeclaration, baseDeclaration: FirProperty): Boolean {
return overrideChecker.isOverriddenProperty(overrideCandidate, baseDeclaration)
}
protected fun similarFunctionsOrBothProperties(
overrideCandidate: FirCallableDeclaration,
baseDeclaration: FirCallableDeclaration
): Boolean {
return when (overrideCandidate) {
is FirSimpleFunction -> when (baseDeclaration) {
is FirSimpleFunction -> isOverriddenFunction(overrideCandidate, baseDeclaration)
is FirProperty -> isOverriddenProperty(overrideCandidate, baseDeclaration)
else -> false
}
is FirConstructor -> false
is FirProperty -> baseDeclaration is FirProperty && isOverriddenProperty(overrideCandidate, baseDeclaration)
is FirField -> baseDeclaration is FirField
else -> error("Unknown fir callable type: $overrideCandidate, $baseDeclaration")
}
}
// Receiver is super-type function here
protected open fun FirCallableSymbol<*>.getOverridden(overrideCandidates: Set<FirCallableSymbol<*>>): FirCallableSymbol<*>? {
val overrideByBaseItem = overrideByBase[this]
@@ -54,10 +29,30 @@ abstract class AbstractFirOverrideScope(
val baseDeclaration = (this as FirBasedSymbol<*>).fir as FirCallableDeclaration
val override = overrideCandidates.firstOrNull {
val overrideCandidate = (it as FirBasedSymbol<*>).fir as FirCallableDeclaration
baseDeclaration.modality != Modality.FINAL && similarFunctionsOrBothProperties(overrideCandidate, baseDeclaration)
baseDeclaration.modality != Modality.FINAL && overrideChecker.similarFunctionsOrBothProperties(
overrideCandidate,
baseDeclaration
)
} // TODO: two or more overrides for one fun?
overrideByBase[this] = override
return override
}
}
internal fun FirOverrideChecker.similarFunctionsOrBothProperties(
overrideCandidate: FirCallableDeclaration,
baseDeclaration: FirCallableDeclaration
): Boolean {
return when (overrideCandidate) {
is FirSimpleFunction -> when (baseDeclaration) {
is FirSimpleFunction -> isOverriddenFunction(overrideCandidate, baseDeclaration)
is FirProperty -> isOverriddenProperty(overrideCandidate, baseDeclaration)
else -> false
}
is FirConstructor -> false
is FirProperty -> baseDeclaration is FirProperty && isOverriddenProperty(overrideCandidate, baseDeclaration)
is FirField -> baseDeclaration is FirField
else -> error("Unknown fir callable type: $overrideCandidate, $baseDeclaration")
}
}
@@ -5,57 +5,40 @@
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.*
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.FirSession
import org.jetbrains.kotlin.fir.resolve.substitution.ConeSubstitutor
import org.jetbrains.kotlin.fir.scopes.*
import org.jetbrains.kotlin.fir.scopes.FirOverrideChecker
import org.jetbrains.kotlin.fir.scopes.FirScope
import org.jetbrains.kotlin.fir.scopes.FirTypeScope
import org.jetbrains.kotlin.fir.scopes.ProcessorAction
import org.jetbrains.kotlin.fir.symbols.impl.*
import org.jetbrains.kotlin.fir.types.*
import org.jetbrains.kotlin.name.CallableId
import org.jetbrains.kotlin.fir.types.ConeSimpleKotlinType
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.types.AbstractTypeChecker
class FirTypeIntersectionScope private constructor(
session: FirSession,
overrideChecker: FirOverrideChecker,
private val scopes: List<FirTypeScope>,
private val dispatchReceiverType: ConeSimpleKotlinType,
dispatchReceiverType: ConeSimpleKotlinType,
) : AbstractFirOverrideScope(session, overrideChecker) {
private val intersectionContext = FirTypeIntersectionScopeContext(session, overrideChecker, scopes, dispatchReceiverType)
private val absentFunctions: MutableSet<Name> = mutableSetOf()
private val absentProperties: MutableSet<Name> = mutableSetOf()
private val absentClassifiers: MutableSet<Name> = mutableSetOf()
private val typeCheckerState = session.typeContext.newTypeCheckerState(
errorTypesEqualToAnything = false,
stubTypesEqualToAnything = false
)
private val overriddenSymbols: MutableMap<FirCallableSymbol<*>, Collection<MemberWithBaseScope<FirCallableSymbol<*>>>> = mutableMapOf()
private val intersectionOverrides =
session.intersectionOverrideStorage.cacheByScope.getValue(dispatchReceiverType).intersectionOverrides
private val callableNamesCached by lazy(LazyThreadSafetyMode.PUBLICATION) {
scopes.flatMapTo(mutableSetOf()) { it.getCallableNames() }
}
override fun processFunctionsByName(name: Name, processor: (FirNamedFunctionSymbol) -> Unit) {
if (!processCallablesByName(name, processor, absentFunctions, FirScope::processFunctionsByName)) {
super.processFunctionsByName(name, processor)
}
processCallablesByName(name, processor, absentFunctions, FirScope::processFunctionsByName)
}
override fun processPropertiesByName(name: Name, processor: (FirVariableSymbol<*>) -> Unit) {
if (!processCallablesByName(name, processor, absentProperties, FirScope::processPropertiesByName)) {
super.processPropertiesByName(name, processor)
}
processCallablesByName(name, processor, absentProperties, FirScope::processPropertiesByName)
}
private inline fun <D : FirCallableSymbol<*>> processCallablesByName(
@@ -63,429 +46,22 @@ class FirTypeIntersectionScope private constructor(
noinline processor: (D) -> Unit,
absentNames: MutableSet<Name>,
processCallables: FirScope.(Name, (D) -> Unit) -> Unit
): Boolean {
if (name in absentNames) {
return false
}
val membersByScope = scopes.mapNotNull { scope ->
val resultForScope = mutableListOf<D>()
scope.processCallables(name) {
if (it !is FirConstructorSymbol) {
resultForScope.add(it)
}
}
resultForScope.takeIf { it.isNotEmpty() }?.let {
scope to it
}
}
if (membersByScope.isEmpty()) {
absentNames.add(name)
return false
}
membersByScope.singleOrNull()?.let { (scope, members) ->
for (member in members) {
overriddenSymbols[member] = listOf(MemberWithBaseScope(member, scope))
processor(member)
}
return false
}
val allMembersWithScope = membersByScope.flatMapTo(linkedSetOf()) { (scope, members) ->
members.map { MemberWithBaseScope(it, scope) }
}
while (allMembersWithScope.size > 1) {
val maxByVisibility = findMemberWithMaxVisibility(allMembersWithScope)
val extractBothWaysWithPrivate = extractBothWaysOverridable(maxByVisibility, allMembersWithScope)
val extractedOverrides = extractBothWaysWithPrivate.filterNot {
Visibilities.isPrivate((it.member.fir as FirMemberDeclaration).visibility)
}.takeIf { it.isNotEmpty() } ?: extractBothWaysWithPrivate
val baseMembersForIntersection = extractedOverrides.calcBaseMembersForIntersectionOverride()
if (baseMembersForIntersection.size > 1) {
val (mostSpecific, scopeForMostSpecific) = selectMostSpecificMember(baseMembersForIntersection)
val intersectionOverride = intersectionOverrides.getValue(
mostSpecific,
FirIntersectionOverrideStorage.ContextForIntersectionOverrideConstruction(
this,
baseMembersForIntersection,
scopeForMostSpecific
)
)
overriddenSymbols[intersectionOverride.member] = extractedOverrides
@Suppress("UNCHECKED_CAST")
processor(intersectionOverride.member as D)
} else {
val mostSpecific = baseMembersForIntersection.single().member
overriddenSymbols[mostSpecific] = extractedOverrides
processor(mostSpecific)
}
}
if (allMembersWithScope.isNotEmpty()) {
val single = allMembersWithScope.single().member
overriddenSymbols[single] = allMembersWithScope.toList()
processor(single)
}
return true
}
private inline fun <reified D : FirCallableDeclaration> D.unwrapSubstitutionOverrides(): D {
var current = this
do {
val next = current.originalForSubstitutionOverride ?: return current
current = next
} while (true)
}
fun <D : FirCallableSymbol<*>> createIntersectionOverride(
extractedOverrides: List<MemberWithBaseScope<D>>,
mostSpecific: D,
scopeForMostSpecific: FirTypeScope
): MemberWithBaseScope<FirCallableSymbol<*>> {
val newModality = chooseIntersectionOverrideModality(extractedOverrides)
val newVisibility = chooseIntersectionVisibility(extractedOverrides)
val extractedOverridesSymbols = extractedOverrides.map { it.member }
return when (mostSpecific) {
is FirNamedFunctionSymbol -> createIntersectionOverride(mostSpecific, extractedOverridesSymbols, newModality, newVisibility)
is FirPropertySymbol -> createIntersectionOverride(mostSpecific, extractedOverridesSymbols, newModality, newVisibility)
else -> throw IllegalStateException("Should not be here")
}.withScope(scopeForMostSpecific)
}
private fun <S : FirCallableSymbol<*>> List<MemberWithBaseScope<S>>.calcBaseMembersForIntersectionOverride(): List<MemberWithBaseScope<S>> {
if (size == 1) return this
val unwrappedMemberSet = mutableSetOf<MemberWithBaseScope<S>>()
for ((member, scope) in this) {
@Suppress("UNCHECKED_CAST")
unwrappedMemberSet += MemberWithBaseScope(member.fir.unwrapSubstitutionOverrides().symbol as S, scope)
}
// If in fact extracted overrides are the same symbols,
// we should just take most specific member without creating intersection
// A typical sample here is inheritance of the same class in different places of hierarchy
if (unwrappedMemberSet.size == 1) {
return listOf(selectMostSpecificMember(this))
}
val baseMembers = mutableSetOf<S>()
for ((member, scope) in this) {
@Suppress("UNCHECKED_CAST")
if (member is FirNamedFunctionSymbol) {
scope.processOverriddenFunctions(member) {
val symbol = it.fir.unwrapSubstitutionOverrides().symbol
if (symbol != member.fir.unwrapSubstitutionOverrides().symbol) {
baseMembers += symbol as S
}
ProcessorAction.NEXT
}
} else if (member is FirPropertySymbol) {
scope.processOverriddenProperties(member) {
val symbol = it.fir.unwrapSubstitutionOverrides().symbol
if (symbol != member.fir.unwrapSubstitutionOverrides().symbol) {
baseMembers += symbol as S
}
ProcessorAction.NEXT
}
}
}
return filterNot { (member, _) -> member.fir.unwrapSubstitutionOverrides().symbol in baseMembers }
}
private fun <D : FirCallableSymbol<*>> chooseIntersectionOverrideModality(
extractedOverridden: Collection<MemberWithBaseScope<D>>
): Modality? {
var hasOpen = false
var hasAbstract = false
for ((member) in extractedOverridden) {
when ((member.fir as FirMemberDeclaration).modality) {
Modality.FINAL -> return Modality.FINAL
Modality.SEALED -> {
// Members should not be sealed. But, that will be reported as WRONG_MODIFIER_TARGET, and here we shouldn't raise an
// internal error. Instead, let the intersection override have the default modality: null.
return null
}
Modality.OPEN -> {
hasOpen = true
}
Modality.ABSTRACT -> {
hasAbstract = true
}
null -> {
}
}
}
if (hasAbstract && !hasOpen) return Modality.ABSTRACT
if (!hasAbstract && hasOpen) return Modality.OPEN
@Suppress("UNCHECKED_CAST")
val processDirectOverridden: ProcessOverriddenWithBaseScope<D> = when (extractedOverridden.first().member) {
is FirNamedFunctionSymbol -> FirTypeScope::processDirectOverriddenFunctionsWithBaseScope as ProcessOverriddenWithBaseScope<D>
is FirPropertySymbol -> FirTypeScope::processDirectOverriddenPropertiesWithBaseScope as ProcessOverriddenWithBaseScope<D>
else -> error("Unexpected callable kind: ${extractedOverridden.first().member}")
}
val realOverridden = extractedOverridden.flatMap { realOverridden(it.member, it.baseScope, processDirectOverridden) }
val filteredOverridden = filterOutOverridden(realOverridden, processDirectOverridden)
return filteredOverridden.minOf { (it.member.fir as FirMemberDeclaration).modality ?: Modality.ABSTRACT }
}
private fun <D : FirCallableSymbol<*>> realOverridden(
symbol: D,
scope: FirTypeScope,
processDirectOverridden: ProcessOverriddenWithBaseScope<D>,
): Collection<MemberWithBaseScope<D>> {
val result = mutableSetOf<MemberWithBaseScope<D>>()
collectRealOverridden(symbol, scope, result, mutableSetOf(), processDirectOverridden)
return result
}
private fun <D : FirCallableSymbol<*>> collectRealOverridden(
symbol: D,
scope: FirTypeScope,
result: MutableCollection<MemberWithBaseScope<D>>,
visited: MutableSet<D>,
processDirectOverridden: FirTypeScope.(D, (D, FirTypeScope) -> ProcessorAction) -> ProcessorAction,
) {
if (!visited.add(symbol)) return
if (!symbol.fir.origin.fromSupertypes) {
result.add(MemberWithBaseScope(symbol, scope))
if (name in absentNames) {
return
}
scope.processDirectOverridden(symbol) { overridden, baseScope ->
collectRealOverridden(overridden, baseScope, result, visited, processDirectOverridden)
ProcessorAction.NEXT
}
}
val callablesWithOverridden = intersectionContext.collectCallables(name, processCallables)
private fun <D : FirCallableSymbol<*>> filterOutOverridden(
extractedOverridden: Collection<MemberWithBaseScope<D>>,
processAllOverridden: ProcessOverriddenWithBaseScope<D>,
): Collection<MemberWithBaseScope<D>> {
return extractedOverridden.filter { overridden1 ->
extractedOverridden.none { overridden2 ->
overridden1 !== overridden2 && overrides(
overridden2,
overridden1,
processAllOverridden
)
}
}
}
// Whether f overrides g
private fun <D : FirCallableSymbol<*>> overrides(
f: MemberWithBaseScope<D>,
g: MemberWithBaseScope<D>,
processAllOverridden: ProcessOverriddenWithBaseScope<D>,
): Boolean {
val (fMember, fScope) = f
val (gMember) = g
var result = false
fScope.processAllOverridden(fMember) { overridden, _ ->
if (overridden == gMember) {
result = true
ProcessorAction.STOP
} else {
ProcessorAction.NEXT
}
if (callablesWithOverridden.isEmpty()) {
absentNames.add(name)
return
}
return result
}
private fun <D : FirCallableSymbol<*>> chooseIntersectionVisibility(
extractedOverrides: Collection<MemberWithBaseScope<D>>
): Visibility {
var maxVisibility: Visibility = Visibilities.Private
for ((override) in extractedOverrides) {
val visibility = (override.fir as FirMemberDeclaration).visibility
// TODO: There is more complex logic at org.jetbrains.kotlin.resolve.OverridingUtil.resolveUnknownVisibilityForMember
// TODO: and org.jetbrains.kotlin.resolve.OverridingUtil.findMaxVisibility
val compare = Visibilities.compare(visibility, maxVisibility) ?: return Visibilities.DEFAULT_VISIBILITY
if (compare > 0) {
maxVisibility = visibility
}
for ((chosenSymbol, overriddenMembers) in callablesWithOverridden) {
overriddenSymbols[chosenSymbol] = overriddenMembers
processor(chosenSymbol)
}
return maxVisibility
}
private fun createIntersectionOverride(
mostSpecific: FirNamedFunctionSymbol,
overrides: Collection<FirCallableSymbol<*>>,
newModality: Modality?,
newVisibility: Visibility,
): FirNamedFunctionSymbol {
val newSymbol =
FirIntersectionOverrideFunctionSymbol(
CallableId(
dispatchReceiverType.classId ?: mostSpecific.dispatchReceiverClassOrNull()?.classId!!,
mostSpecific.fir.name
),
overrides
)
val mostSpecificFunction = mostSpecific.fir
FirFakeOverrideGenerator.createCopyForFirFunction(
newSymbol,
mostSpecificFunction, session, FirDeclarationOrigin.IntersectionOverride,
mostSpecificFunction.isExpect,
newDispatchReceiverType = dispatchReceiverType,
newModality = newModality,
newVisibility = newVisibility,
).apply {
originalForIntersectionOverrideAttr = mostSpecific.fir
}
return newSymbol
}
private fun createIntersectionOverride(
mostSpecific: FirPropertySymbol,
overrides: Collection<FirCallableSymbol<*>>,
newModality: Modality?,
newVisibility: Visibility,
): FirPropertySymbol {
val callableId = CallableId(
dispatchReceiverType.classId ?: mostSpecific.dispatchReceiverClassOrNull()?.classId!!,
mostSpecific.fir.name
)
val newSymbol = FirIntersectionOverridePropertySymbol(callableId, overrides)
val mostSpecificProperty = mostSpecific.fir
FirFakeOverrideGenerator.createCopyForFirProperty(
newSymbol, mostSpecificProperty, session, FirDeclarationOrigin.IntersectionOverride,
newModality = newModality,
newVisibility = newVisibility,
newDispatchReceiverType = dispatchReceiverType,
).apply {
originalForIntersectionOverrideAttr = mostSpecific.fir
}
return newSymbol
}
private fun <D : FirCallableSymbol<*>> selectMostSpecificMember(overridables: Collection<MemberWithBaseScope<D>>): MemberWithBaseScope<D> {
require(overridables.isNotEmpty()) { "Should have at least one overridable symbol" }
if (overridables.size == 1) {
return overridables.first()
}
val candidates: MutableCollection<MemberWithBaseScope<D>> = ArrayList(2)
var transitivelyMostSpecific: MemberWithBaseScope<D> = overridables.first()
for (candidate in overridables) {
if (overridables.all { hasMoreSpecificOrSameSignature(candidate.member, it.member) }) {
candidates.add(candidate)
}
if (hasMoreSpecificOrSameSignature(candidate.member, transitivelyMostSpecific.member) &&
!hasMoreSpecificOrSameSignature(transitivelyMostSpecific.member, candidate.member)
) {
transitivelyMostSpecific = candidate
}
}
return when {
candidates.isEmpty() -> transitivelyMostSpecific
candidates.size == 1 -> candidates.first()
else -> {
candidates.firstOrNull {
val type = it.member.fir.returnTypeRef.coneTypeSafe<ConeKotlinType>()
type != null && type !is ConeFlexibleType
}?.let { return it }
candidates.first()
}
}
}
private fun hasMoreSpecificOrSameSignature(
a: FirCallableSymbol<*>,
b: FirCallableSymbol<*>
): Boolean {
if (a === b) return true
val aFir = a.fir
val bFir = b.fir
val substitutor = buildSubstitutorForOverridesCheck(aFir, bFir, session) ?: return false
// NB: these lines throw CCE in modularized tests when changed to just .coneType (FirImplicitTypeRef)
val aReturnType = a.fir.returnTypeRef.coneTypeSafe<ConeKotlinType>()?.let(substitutor::substituteOrSelf) ?: return false
val bReturnType = b.fir.returnTypeRef.coneTypeSafe<ConeKotlinType>() ?: return false
if (aFir is FirSimpleFunction) {
require(bFir is FirSimpleFunction) { "b is " + b.javaClass }
return isTypeMoreSpecificOrSame(aReturnType, bReturnType)
}
if (aFir is FirProperty) {
require(bFir is FirProperty) { "b is " + b.javaClass }
// TODO: if (!OverridingUtil.isAccessorMoreSpecific(pa.getSetter(), pb.getSetter())) return false
return if (aFir.isVar && bFir.isVar) {
AbstractTypeChecker.equalTypes(typeCheckerState, aReturnType, bReturnType)
} else { // both vals or var vs val: val can't be more specific then var
!(!aFir.isVar && bFir.isVar) && isTypeMoreSpecificOrSame(aReturnType, bReturnType)
}
}
throw IllegalArgumentException("Unexpected callable: " + a.javaClass)
}
private fun isTypeMoreSpecificOrSame(a: ConeKotlinType, b: ConeKotlinType): Boolean =
AbstractTypeChecker.isSubtypeOf(typeCheckerState, a, b)
private fun <D : FirCallableSymbol<*>> findMemberWithMaxVisibility(members: Collection<MemberWithBaseScope<D>>): MemberWithBaseScope<D> {
assert(members.isNotEmpty())
var member: MemberWithBaseScope<D>? = null
for (candidate in members) {
if (member == null) {
member = candidate
continue
}
val result = Visibilities.compare(
member.member.fir.status.visibility,
candidate.member.fir.status.visibility
)
if (result != null && result < 0) {
member = candidate
}
}
return member!!
}
private fun <D : FirCallableSymbol<*>> extractBothWaysOverridable(
overrider: MemberWithBaseScope<D>,
members: MutableCollection<MemberWithBaseScope<D>>
): List<MemberWithBaseScope<D>> {
val result = mutableListOf<MemberWithBaseScope<D>>().apply { add(overrider) }
val iterator = members.iterator()
val overrideCandidate = overrider.member.fir
while (iterator.hasNext()) {
val next = iterator.next()
if (next == overrider) {
iterator.remove()
continue
}
if (similarFunctionsOrBothProperties(overrideCandidate, next.member.fir)) {
result.add(next)
iterator.remove()
}
}
return result
}
override fun processClassifiersByNameWithSubstitution(name: Name, processor: (FirClassifierSymbol<*>, ConeSubstitutor) -> Unit) {
@@ -514,7 +90,7 @@ class FirTypeIntersectionScope private constructor(
@Suppress("UNCHECKED_CAST")
fun <S : FirCallableSymbol<*>> getDirectOverriddenSymbols(symbol: S): Collection<MemberWithBaseScope<S>> {
val intersectionOverride = intersectionOverrides.getValueIfComputed(symbol)
val intersectionOverride = intersectionContext.intersectionOverrides.getValueIfComputed(symbol)
val allDirectOverridden = overriddenSymbols[symbol].orEmpty() + intersectionOverride?.let {
overriddenSymbols[it.member]
}.orEmpty()
@@ -576,41 +152,3 @@ class FirTypeIntersectionScope private constructor(
}
}
}
class MemberWithBaseScope<out D : FirCallableSymbol<*>>(val member: D, val baseScope: FirTypeScope) {
operator fun component1() = member
operator fun component2() = baseScope
override fun equals(other: Any?): Boolean {
return other is MemberWithBaseScope<*> && member == other.member
}
override fun hashCode(): Int {
return member.hashCode()
}
}
private fun <D : FirCallableSymbol<*>> D.withScope(baseScope: FirTypeScope) = MemberWithBaseScope(this, baseScope)
class FirIntersectionOverrideStorage(val session: FirSession) : FirSessionComponent {
private val cachesFactory = session.firCachesFactory
class CacheForScope(cachesFactory: FirCachesFactory) {
val intersectionOverrides: FirCache<FirCallableSymbol<*>, MemberWithBaseScope<FirCallableSymbol<*>>, ContextForIntersectionOverrideConstruction<*>> =
cachesFactory.createCache { mostSpecific, context ->
val (intersectionScope, extractedOverrides, scopeForMostSpecific) = context
intersectionScope.createIntersectionOverride(extractedOverrides, mostSpecific, scopeForMostSpecific)
}
}
data class ContextForIntersectionOverrideConstruction<D : FirCallableSymbol<*>>(
val intersectionScope: FirTypeIntersectionScope,
val extractedOverrides: List<MemberWithBaseScope<D>>,
val scopeForMostSpecific: FirTypeScope
)
val cacheByScope: FirCache<ConeKotlinType, CacheForScope, Nothing?> =
cachesFactory.createCache { _ -> CacheForScope(cachesFactory) }
}
private val FirSession.intersectionOverrideStorage: FirIntersectionOverrideStorage by FirSession.sessionComponentAccessor()
@@ -0,0 +1,512 @@
/*
* Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
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.*
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.scopes.*
import org.jetbrains.kotlin.fir.scopes.impl.FirIntersectionOverrideStorage.ContextForIntersectionOverrideConstruction
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.types.AbstractTypeChecker
class FirTypeIntersectionScopeContext(
val session: FirSession,
private val overrideChecker: FirOverrideChecker,
@property:PrivateForInline val scopes: List<FirTypeScope>,
private val dispatchReceiverType: ConeSimpleKotlinType,
) {
private val typeCheckerState = session.typeContext.newTypeCheckerState(
errorTypesEqualToAnything = false,
stubTypesEqualToAnything = false
)
val intersectionOverrides: FirCache<FirCallableSymbol<*>, MemberWithBaseScope<FirCallableSymbol<*>>, ContextForIntersectionOverrideConstruction<*>> =
session.intersectionOverrideStorage.cacheByScope.getValue(dispatchReceiverType).intersectionOverrides
data class ResultOfIntersection<D : FirCallableSymbol<*>>(
val chosenSymbol: D,
val overriddenMembers: List<MemberWithBaseScope<D>>
) {
constructor(
chosenSymbol: D,
overriddenMember: MemberWithBaseScope<D>
) : this(chosenSymbol, listOf(overriddenMember))
}
@OptIn(PrivateForInline::class)
inline fun <D : FirCallableSymbol<*>> collectCallables(
name: Name,
processCallables: FirScope.(Name, (D) -> Unit) -> Unit
): List<ResultOfIntersection<D>> {
val membersByScope = scopes.mapNotNull { scope ->
val resultForScope = mutableListOf<D>()
scope.processCallables(name) {
if (it !is FirConstructorSymbol) {
resultForScope.add(it)
}
}
resultForScope.takeIf { it.isNotEmpty() }?.let {
scope to it
}
}
return collectCallablesImpl(membersByScope)
}
@PrivateForInline
fun <D : FirCallableSymbol<*>> collectCallablesImpl(
membersByScope: List<Pair<FirTypeScope, MutableList<D>>>
): List<ResultOfIntersection<D>> {
if (membersByScope.isEmpty()) {
return emptyList()
}
membersByScope.singleOrNull()?.let { (scope, members) ->
return members.map { ResultOfIntersection(it, MemberWithBaseScope(it, scope)) }
}
val allMembersWithScope = membersByScope.flatMapTo(linkedSetOf()) { (scope, members) ->
members.map { MemberWithBaseScope(it, scope) }
}
val result = mutableListOf<ResultOfIntersection<D>>()
while (allMembersWithScope.size > 1) {
val maxByVisibility = findMemberWithMaxVisibility(allMembersWithScope)
val extractBothWaysWithPrivate = extractBothWaysOverridable(maxByVisibility, allMembersWithScope)
val extractedOverrides = extractBothWaysWithPrivate.filterNotTo(mutableListOf()) {
Visibilities.isPrivate((it.member.fir as FirMemberDeclaration).visibility)
}.takeIf { it.isNotEmpty() } ?: extractBothWaysWithPrivate
val baseMembersForIntersection = extractedOverrides.calcBaseMembersForIntersectionOverride()
if (baseMembersForIntersection.size > 1) {
val (mostSpecific, scopeForMostSpecific) = selectMostSpecificMember(baseMembersForIntersection)
val intersectionOverride = intersectionOverrides.getValue(
mostSpecific,
ContextForIntersectionOverrideConstruction(
this,
extractedOverrides,
scopeForMostSpecific
)
)
@Suppress("UNCHECKED_CAST")
result += ResultOfIntersection(intersectionOverride.member as D, extractedOverrides)
} else {
val mostSpecific = baseMembersForIntersection.single().member
result += ResultOfIntersection(mostSpecific, extractedOverrides)
}
}
if (allMembersWithScope.isNotEmpty()) {
val single = allMembersWithScope.single().member
result += ResultOfIntersection(single, allMembersWithScope.toList())
}
return result
}
fun <D : FirCallableSymbol<*>> createIntersectionOverride(
extractedOverrides: List<MemberWithBaseScope<D>>,
mostSpecific: D,
scopeForMostSpecific: FirTypeScope
): MemberWithBaseScope<FirCallableSymbol<*>> {
val newModality = chooseIntersectionOverrideModality(extractedOverrides)
val newVisibility = chooseIntersectionVisibility(extractedOverrides)
val extractedOverridesSymbols = extractedOverrides.map { it.member }
return when (mostSpecific) {
is FirNamedFunctionSymbol -> createIntersectionOverride(mostSpecific, extractedOverridesSymbols, newModality, newVisibility)
is FirPropertySymbol -> createIntersectionOverride(mostSpecific, extractedOverridesSymbols, newModality, newVisibility)
else -> throw IllegalStateException("Should not be here")
}.withScope(scopeForMostSpecific)
}
private fun <S : FirCallableSymbol<*>> List<MemberWithBaseScope<S>>.calcBaseMembersForIntersectionOverride(): List<MemberWithBaseScope<S>> {
if (size == 1) return this
val unwrappedMemberSet = mutableSetOf<MemberWithBaseScope<S>>()
for ((member, scope) in this) {
@Suppress("UNCHECKED_CAST")
unwrappedMemberSet += MemberWithBaseScope(member.fir.unwrapSubstitutionOverrides().symbol as S, scope)
}
// If in fact extracted overrides are the same symbols,
// we should just take most specific member without creating intersection
// A typical sample here is inheritance of the same class in different places of hierarchy
if (unwrappedMemberSet.size == 1) {
return listOf(selectMostSpecificMember(this))
}
val baseMembers = mutableSetOf<S>()
for ((member, scope) in this) {
@Suppress("UNCHECKED_CAST")
if (member is FirNamedFunctionSymbol) {
scope.processOverriddenFunctions(member) {
val symbol = it.fir.unwrapSubstitutionOverrides().symbol
if (symbol != member.fir.unwrapSubstitutionOverrides().symbol) {
baseMembers += symbol as S
}
ProcessorAction.NEXT
}
} else if (member is FirPropertySymbol) {
scope.processOverriddenProperties(member) {
val symbol = it.fir.unwrapSubstitutionOverrides().symbol
if (symbol != member.fir.unwrapSubstitutionOverrides().symbol) {
baseMembers += symbol as S
}
ProcessorAction.NEXT
}
}
}
val result = this.toMutableList()
result.removeIf { (member, _) -> member.fir.unwrapSubstitutionOverrides().symbol in baseMembers }
return result
}
private fun <D : FirCallableSymbol<*>> selectMostSpecificMember(overridables: Collection<MemberWithBaseScope<D>>): MemberWithBaseScope<D> {
require(overridables.isNotEmpty()) { "Should have at least one overridable symbol" }
if (overridables.size == 1) {
return overridables.first()
}
val candidates: MutableCollection<MemberWithBaseScope<D>> = ArrayList(2)
var transitivelyMostSpecific: MemberWithBaseScope<D> = overridables.first()
for (candidate in overridables) {
if (overridables.all { isMoreSpecific(candidate.member, it.member) }) {
candidates.add(candidate)
}
if (isMoreSpecific(candidate.member, transitivelyMostSpecific.member) &&
!isMoreSpecific(transitivelyMostSpecific.member, candidate.member)
) {
transitivelyMostSpecific = candidate
}
}
return when {
candidates.isEmpty() -> transitivelyMostSpecific
candidates.size == 1 -> candidates.first()
else -> {
candidates.firstOrNull {
val type = it.member.fir.returnTypeRef.coneTypeSafe<ConeKotlinType>()
type != null && type !is ConeFlexibleType
}?.let { return it }
candidates.first()
}
}
}
private fun isMoreSpecific(
a: FirCallableSymbol<*>,
b: FirCallableSymbol<*>
): Boolean {
val aFir = a.fir
val bFir = b.fir
val substitutor = buildSubstitutorForOverridesCheck(aFir, bFir, session) ?: return false
// NB: these lines throw CCE in modularized tests when changed to just .coneType (FirImplicitTypeRef)
val aReturnType = a.fir.returnTypeRef.coneTypeSafe<ConeKotlinType>()?.let(substitutor::substituteOrSelf) ?: return false
val bReturnType = b.fir.returnTypeRef.coneTypeSafe<ConeKotlinType>() ?: return false
if (aFir is FirSimpleFunction) {
require(bFir is FirSimpleFunction) { "b is " + b.javaClass }
return isTypeMoreSpecific(aReturnType, bReturnType)
}
if (aFir is FirProperty) {
require(bFir is FirProperty) { "b is " + b.javaClass }
// TODO: if (!OverridingUtil.isAccessorMoreSpecific(pa.getSetter(), pb.getSetter())) return false
return if (aFir.isVar && bFir.isVar) {
AbstractTypeChecker.equalTypes(typeCheckerState, aReturnType, bReturnType)
} else { // both vals or var vs val: val can't be more specific then var
!(!aFir.isVar && bFir.isVar) && isTypeMoreSpecific(aReturnType, bReturnType)
}
}
throw IllegalArgumentException("Unexpected callable: " + a.javaClass)
}
private fun isTypeMoreSpecific(a: ConeKotlinType, b: ConeKotlinType): Boolean =
AbstractTypeChecker.isSubtypeOf(typeCheckerState, a, b)
private fun <D : FirCallableSymbol<*>> findMemberWithMaxVisibility(members: Collection<MemberWithBaseScope<D>>): MemberWithBaseScope<D> {
assert(members.isNotEmpty())
var member: MemberWithBaseScope<D>? = null
for (candidate in members) {
if (member == null) {
member = candidate
continue
}
val result = Visibilities.compare(
member.member.fir.status.visibility,
candidate.member.fir.status.visibility
)
if (result != null && result < 0) {
member = candidate
}
}
return member!!
}
private fun <D : FirCallableSymbol<*>> extractBothWaysOverridable(
overrider: MemberWithBaseScope<D>,
members: MutableCollection<MemberWithBaseScope<D>>
): MutableList<MemberWithBaseScope<D>> {
val result = mutableListOf<MemberWithBaseScope<D>>().apply { add(overrider) }
val iterator = members.iterator()
val overrideCandidate = overrider.member.fir
while (iterator.hasNext()) {
val next = iterator.next()
if (next == overrider) {
iterator.remove()
continue
}
if (overrideChecker.similarFunctionsOrBothProperties(overrideCandidate, next.member.fir)) {
result.add(next)
iterator.remove()
}
}
return result
}
private fun <D : FirCallableSymbol<*>> chooseIntersectionOverrideModality(
extractedOverridden: Collection<MemberWithBaseScope<D>>
): Modality? {
var hasOpen = false
var hasAbstract = false
for ((member) in extractedOverridden) {
when ((member.fir as FirMemberDeclaration).modality) {
Modality.FINAL -> return Modality.FINAL
Modality.SEALED -> {
// Members should not be sealed. But, that will be reported as WRONG_MODIFIER_TARGET, and here we shouldn't raise an
// internal error. Instead, let the intersection override have the default modality: null.
return null
}
Modality.OPEN -> {
hasOpen = true
}
Modality.ABSTRACT -> {
hasAbstract = true
}
null -> {
}
}
}
if (hasAbstract && !hasOpen) return Modality.ABSTRACT
if (!hasAbstract && hasOpen) return Modality.OPEN
@Suppress("UNCHECKED_CAST")
val processDirectOverridden: ProcessOverriddenWithBaseScope<D> = when (extractedOverridden.first().member) {
is FirNamedFunctionSymbol -> FirTypeScope::processDirectOverriddenFunctionsWithBaseScope as ProcessOverriddenWithBaseScope<D>
is FirPropertySymbol -> FirTypeScope::processDirectOverriddenPropertiesWithBaseScope as ProcessOverriddenWithBaseScope<D>
else -> error("Unexpected callable kind: ${extractedOverridden.first().member}")
}
val realOverridden = extractedOverridden.flatMap { realOverridden(it.member, it.baseScope, processDirectOverridden) }
val filteredOverridden = filterOutOverridden(realOverridden, processDirectOverridden)
return filteredOverridden.minOf { (it.member.fir as FirMemberDeclaration).modality ?: Modality.ABSTRACT }
}
private fun <D : FirCallableSymbol<*>> realOverridden(
symbol: D,
scope: FirTypeScope,
processDirectOverridden: ProcessOverriddenWithBaseScope<D>,
): Collection<MemberWithBaseScope<D>> {
val result = mutableSetOf<MemberWithBaseScope<D>>()
collectRealOverridden(symbol, scope, result, mutableSetOf(), processDirectOverridden)
return result
}
private inline fun <reified D : FirCallableDeclaration> D.unwrapSubstitutionOverrides(): D {
var current = this
do {
val next = current.originalForSubstitutionOverride ?: return current
current = next
} while (true)
}
private fun <D : FirCallableSymbol<*>> collectRealOverridden(
symbol: D,
scope: FirTypeScope,
result: MutableCollection<MemberWithBaseScope<D>>,
visited: MutableSet<D>,
processDirectOverridden: FirTypeScope.(D, (D, FirTypeScope) -> ProcessorAction) -> ProcessorAction,
) {
if (!visited.add(symbol)) return
if (!symbol.fir.origin.fromSupertypes) {
result.add(MemberWithBaseScope(symbol, scope))
return
}
scope.processDirectOverridden(symbol) { overridden, baseScope ->
collectRealOverridden(overridden, baseScope, result, visited, processDirectOverridden)
ProcessorAction.NEXT
}
}
private fun <D : FirCallableSymbol<*>> filterOutOverridden(
extractedOverridden: Collection<MemberWithBaseScope<D>>,
processAllOverridden: ProcessOverriddenWithBaseScope<D>,
): Collection<MemberWithBaseScope<D>> {
return extractedOverridden.filter { overridden1 ->
extractedOverridden.none { overridden2 ->
overridden1 !== overridden2 && overrides(
overridden2,
overridden1,
processAllOverridden
)
}
}
}
// Whether f overrides g
private fun <D : FirCallableSymbol<*>> overrides(
f: MemberWithBaseScope<D>,
g: MemberWithBaseScope<D>,
processAllOverridden: ProcessOverriddenWithBaseScope<D>,
): Boolean {
val (fMember, fScope) = f
val (gMember) = g
var result = false
fScope.processAllOverridden(fMember) { overridden, _ ->
if (overridden == gMember) {
result = true
ProcessorAction.STOP
} else {
ProcessorAction.NEXT
}
}
return result
}
private fun <D : FirCallableSymbol<*>> chooseIntersectionVisibility(
extractedOverrides: Collection<MemberWithBaseScope<D>>
): Visibility {
var maxVisibility: Visibility = Visibilities.Private
for ((override) in extractedOverrides) {
val visibility = (override.fir as FirMemberDeclaration).visibility
// TODO: There is more complex logic at org.jetbrains.kotlin.resolve.OverridingUtil.resolveUnknownVisibilityForMember
// TODO: and org.jetbrains.kotlin.resolve.OverridingUtil.findMaxVisibility
val compare = Visibilities.compare(visibility, maxVisibility) ?: return Visibilities.DEFAULT_VISIBILITY
if (compare > 0) {
maxVisibility = visibility
}
}
return maxVisibility
}
private fun createIntersectionOverride(
mostSpecific: FirNamedFunctionSymbol,
overrides: Collection<FirCallableSymbol<*>>,
newModality: Modality?,
newVisibility: Visibility,
): FirNamedFunctionSymbol {
val newSymbol =
FirIntersectionOverrideFunctionSymbol(
CallableId(
dispatchReceiverType.classId ?: mostSpecific.dispatchReceiverClassOrNull()?.classId!!,
mostSpecific.fir.name
),
overrides
)
val mostSpecificFunction = mostSpecific.fir
FirFakeOverrideGenerator.createCopyForFirFunction(
newSymbol,
mostSpecificFunction, session, FirDeclarationOrigin.IntersectionOverride,
mostSpecificFunction.isExpect,
newDispatchReceiverType = dispatchReceiverType,
newModality = newModality,
newVisibility = newVisibility,
).apply {
originalForIntersectionOverrideAttr = mostSpecific.fir
}
return newSymbol
}
private fun createIntersectionOverride(
mostSpecific: FirPropertySymbol,
overrides: Collection<FirCallableSymbol<*>>,
newModality: Modality?,
newVisibility: Visibility,
): FirPropertySymbol {
val callableId = CallableId(
dispatchReceiverType.classId ?: mostSpecific.dispatchReceiverClassOrNull()?.classId!!,
mostSpecific.fir.name
)
val newSymbol = FirIntersectionOverridePropertySymbol(callableId, overrides)
val mostSpecificProperty = mostSpecific.fir
FirFakeOverrideGenerator.createCopyForFirProperty(
newSymbol, mostSpecificProperty, session, FirDeclarationOrigin.IntersectionOverride,
newModality = newModality,
newVisibility = newVisibility,
newDispatchReceiverType = dispatchReceiverType,
).apply {
originalForIntersectionOverrideAttr = mostSpecific.fir
}
return newSymbol
}
}
class MemberWithBaseScope<out D : FirCallableSymbol<*>>(val member: D, val baseScope: FirTypeScope) {
operator fun component1() = member
operator fun component2() = baseScope
override fun equals(other: Any?): Boolean {
return other is MemberWithBaseScope<*> && member == other.member
}
override fun hashCode(): Int {
return member.hashCode()
}
}
private fun <D : FirCallableSymbol<*>> D.withScope(baseScope: FirTypeScope) = MemberWithBaseScope(this, baseScope)
class FirIntersectionOverrideStorage(val session: FirSession) : FirSessionComponent {
private val cachesFactory = session.firCachesFactory
class CacheForScope(cachesFactory: FirCachesFactory) {
val intersectionOverrides: FirCache<FirCallableSymbol<*>, MemberWithBaseScope<FirCallableSymbol<*>>, ContextForIntersectionOverrideConstruction<*>> =
cachesFactory.createCache { mostSpecific, context ->
val (intersectionScope, extractedOverrides, scopeForMostSpecific) = context
intersectionScope.createIntersectionOverride(extractedOverrides, mostSpecific, scopeForMostSpecific)
}
}
data class ContextForIntersectionOverrideConstruction<D : FirCallableSymbol<*>>(
val intersectionContext: FirTypeIntersectionScopeContext,
val extractedOverrides: List<MemberWithBaseScope<D>>,
val scopeForMostSpecific: FirTypeScope
)
val cacheByScope: FirCache<ConeKotlinType, CacheForScope, Nothing?> =
cachesFactory.createCache { _ -> CacheForScope(cachesFactory) }
}
private val FirSession.intersectionOverrideStorage: FirIntersectionOverrideStorage by FirSession.sessionComponentAccessor()