diff --git a/compiler/fir/providers/src/org/jetbrains/kotlin/fir/scopes/impl/AbstractFirOverrideScope.kt b/compiler/fir/providers/src/org/jetbrains/kotlin/fir/scopes/impl/AbstractFirOverrideScope.kt index 91041749af5..f23ce25a89d 100644 --- a/compiler/fir/providers/src/org/jetbrains/kotlin/fir/scopes/impl/AbstractFirOverrideScope.kt +++ b/compiler/fir/providers/src/org/jetbrains/kotlin/fir/scopes/impl/AbstractFirOverrideScope.kt @@ -21,31 +21,6 @@ abstract class AbstractFirOverrideScope( //base symbol as key, overridden as value val overrideByBase = mutableMapOf, 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<*>? { 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") + } +} diff --git a/compiler/fir/providers/src/org/jetbrains/kotlin/fir/scopes/impl/FirTypeIntersectionScope.kt b/compiler/fir/providers/src/org/jetbrains/kotlin/fir/scopes/impl/FirTypeIntersectionScope.kt index ed3638de330..733698612df 100644 --- a/compiler/fir/providers/src/org/jetbrains/kotlin/fir/scopes/impl/FirTypeIntersectionScope.kt +++ b/compiler/fir/providers/src/org/jetbrains/kotlin/fir/scopes/impl/FirTypeIntersectionScope.kt @@ -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, - private val dispatchReceiverType: ConeSimpleKotlinType, + dispatchReceiverType: ConeSimpleKotlinType, ) : AbstractFirOverrideScope(session, overrideChecker) { + private val intersectionContext = FirTypeIntersectionScopeContext(session, overrideChecker, scopes, dispatchReceiverType) + private val absentFunctions: MutableSet = mutableSetOf() private val absentProperties: MutableSet = mutableSetOf() private val absentClassifiers: MutableSet = mutableSetOf() - private val typeCheckerState = session.typeContext.newTypeCheckerState( - errorTypesEqualToAnything = false, - stubTypesEqualToAnything = false - ) - private val overriddenSymbols: MutableMap, Collection>>> = 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 > processCallablesByName( @@ -63,429 +46,22 @@ class FirTypeIntersectionScope private constructor( noinline processor: (D) -> Unit, absentNames: MutableSet, processCallables: FirScope.(Name, (D) -> Unit) -> Unit - ): Boolean { - if (name in absentNames) { - return false - } - - val membersByScope = scopes.mapNotNull { scope -> - val resultForScope = mutableListOf() - 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 D.unwrapSubstitutionOverrides(): D { - var current = this - - do { - val next = current.originalForSubstitutionOverride ?: return current - current = next - } while (true) - } - - fun > createIntersectionOverride( - extractedOverrides: List>, - mostSpecific: D, - scopeForMostSpecific: FirTypeScope - ): MemberWithBaseScope> { - 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 > List>.calcBaseMembersForIntersectionOverride(): List> { - if (size == 1) return this - val unwrappedMemberSet = mutableSetOf>() - 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() - 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 > chooseIntersectionOverrideModality( - extractedOverridden: Collection> - ): 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 = when (extractedOverridden.first().member) { - is FirNamedFunctionSymbol -> FirTypeScope::processDirectOverriddenFunctionsWithBaseScope as ProcessOverriddenWithBaseScope - is FirPropertySymbol -> FirTypeScope::processDirectOverriddenPropertiesWithBaseScope as ProcessOverriddenWithBaseScope - 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 > realOverridden( - symbol: D, - scope: FirTypeScope, - processDirectOverridden: ProcessOverriddenWithBaseScope, - ): Collection> { - val result = mutableSetOf>() - - collectRealOverridden(symbol, scope, result, mutableSetOf(), processDirectOverridden) - - return result - } - - private fun > collectRealOverridden( - symbol: D, - scope: FirTypeScope, - result: MutableCollection>, - visited: MutableSet, - 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 > filterOutOverridden( - extractedOverridden: Collection>, - processAllOverridden: ProcessOverriddenWithBaseScope, - ): Collection> { - return extractedOverridden.filter { overridden1 -> - extractedOverridden.none { overridden2 -> - overridden1 !== overridden2 && overrides( - overridden2, - overridden1, - processAllOverridden - ) - } - } - } - - // Whether f overrides g - private fun > overrides( - f: MemberWithBaseScope, - g: MemberWithBaseScope, - processAllOverridden: ProcessOverriddenWithBaseScope, - ): 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 > chooseIntersectionVisibility( - extractedOverrides: Collection> - ): 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>, - 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>, - 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 > selectMostSpecificMember(overridables: Collection>): MemberWithBaseScope { - require(overridables.isNotEmpty()) { "Should have at least one overridable symbol" } - if (overridables.size == 1) { - return overridables.first() - } - - val candidates: MutableCollection> = ArrayList(2) - var transitivelyMostSpecific: MemberWithBaseScope = 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() - 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()?.let(substitutor::substituteOrSelf) ?: return false - val bReturnType = b.fir.returnTypeRef.coneTypeSafe() ?: 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 > findMemberWithMaxVisibility(members: Collection>): MemberWithBaseScope { - assert(members.isNotEmpty()) - - var member: MemberWithBaseScope? = 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 > extractBothWaysOverridable( - overrider: MemberWithBaseScope, - members: MutableCollection> - ): List> { - val result = mutableListOf>().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 > getDirectOverriddenSymbols(symbol: S): Collection> { - 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>(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.withScope(baseScope: FirTypeScope) = MemberWithBaseScope(this, baseScope) - -class FirIntersectionOverrideStorage(val session: FirSession) : FirSessionComponent { - private val cachesFactory = session.firCachesFactory - - class CacheForScope(cachesFactory: FirCachesFactory) { - val intersectionOverrides: FirCache, MemberWithBaseScope>, ContextForIntersectionOverrideConstruction<*>> = - cachesFactory.createCache { mostSpecific, context -> - val (intersectionScope, extractedOverrides, scopeForMostSpecific) = context - intersectionScope.createIntersectionOverride(extractedOverrides, mostSpecific, scopeForMostSpecific) - } - } - - data class ContextForIntersectionOverrideConstruction>( - val intersectionScope: FirTypeIntersectionScope, - val extractedOverrides: List>, - val scopeForMostSpecific: FirTypeScope - ) - - val cacheByScope: FirCache = - cachesFactory.createCache { _ -> CacheForScope(cachesFactory) } -} - -private val FirSession.intersectionOverrideStorage: FirIntersectionOverrideStorage by FirSession.sessionComponentAccessor() diff --git a/compiler/fir/providers/src/org/jetbrains/kotlin/fir/scopes/impl/FirTypeIntersectionScopeContext.kt b/compiler/fir/providers/src/org/jetbrains/kotlin/fir/scopes/impl/FirTypeIntersectionScopeContext.kt new file mode 100644 index 00000000000..a256a357ec1 --- /dev/null +++ b/compiler/fir/providers/src/org/jetbrains/kotlin/fir/scopes/impl/FirTypeIntersectionScopeContext.kt @@ -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, + private val dispatchReceiverType: ConeSimpleKotlinType, +) { + private val typeCheckerState = session.typeContext.newTypeCheckerState( + errorTypesEqualToAnything = false, + stubTypesEqualToAnything = false + ) + + val intersectionOverrides: FirCache, MemberWithBaseScope>, ContextForIntersectionOverrideConstruction<*>> = + session.intersectionOverrideStorage.cacheByScope.getValue(dispatchReceiverType).intersectionOverrides + + data class ResultOfIntersection>( + val chosenSymbol: D, + val overriddenMembers: List> + ) { + constructor( + chosenSymbol: D, + overriddenMember: MemberWithBaseScope + ) : this(chosenSymbol, listOf(overriddenMember)) + } + + @OptIn(PrivateForInline::class) + inline fun > collectCallables( + name: Name, + processCallables: FirScope.(Name, (D) -> Unit) -> Unit + ): List> { + val membersByScope = scopes.mapNotNull { scope -> + val resultForScope = mutableListOf() + scope.processCallables(name) { + if (it !is FirConstructorSymbol) { + resultForScope.add(it) + } + } + + resultForScope.takeIf { it.isNotEmpty() }?.let { + scope to it + } + } + return collectCallablesImpl(membersByScope) + } + + @PrivateForInline + fun > collectCallablesImpl( + membersByScope: List>> + ): List> { + 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>() + + 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 > createIntersectionOverride( + extractedOverrides: List>, + mostSpecific: D, + scopeForMostSpecific: FirTypeScope + ): MemberWithBaseScope> { + 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 > List>.calcBaseMembersForIntersectionOverride(): List> { + if (size == 1) return this + val unwrappedMemberSet = mutableSetOf>() + 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() + 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 > selectMostSpecificMember(overridables: Collection>): MemberWithBaseScope { + require(overridables.isNotEmpty()) { "Should have at least one overridable symbol" } + if (overridables.size == 1) { + return overridables.first() + } + + val candidates: MutableCollection> = ArrayList(2) + var transitivelyMostSpecific: MemberWithBaseScope = 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() + 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()?.let(substitutor::substituteOrSelf) ?: return false + val bReturnType = b.fir.returnTypeRef.coneTypeSafe() ?: 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 > findMemberWithMaxVisibility(members: Collection>): MemberWithBaseScope { + assert(members.isNotEmpty()) + + var member: MemberWithBaseScope? = 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 > extractBothWaysOverridable( + overrider: MemberWithBaseScope, + members: MutableCollection> + ): MutableList> { + val result = mutableListOf>().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 > chooseIntersectionOverrideModality( + extractedOverridden: Collection> + ): 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 = when (extractedOverridden.first().member) { + is FirNamedFunctionSymbol -> FirTypeScope::processDirectOverriddenFunctionsWithBaseScope as ProcessOverriddenWithBaseScope + is FirPropertySymbol -> FirTypeScope::processDirectOverriddenPropertiesWithBaseScope as ProcessOverriddenWithBaseScope + 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 > realOverridden( + symbol: D, + scope: FirTypeScope, + processDirectOverridden: ProcessOverriddenWithBaseScope, + ): Collection> { + val result = mutableSetOf>() + + collectRealOverridden(symbol, scope, result, mutableSetOf(), processDirectOverridden) + + return result + } + + private inline fun D.unwrapSubstitutionOverrides(): D { + var current = this + + do { + val next = current.originalForSubstitutionOverride ?: return current + current = next + } while (true) + } + + private fun > collectRealOverridden( + symbol: D, + scope: FirTypeScope, + result: MutableCollection>, + visited: MutableSet, + 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 > filterOutOverridden( + extractedOverridden: Collection>, + processAllOverridden: ProcessOverriddenWithBaseScope, + ): Collection> { + return extractedOverridden.filter { overridden1 -> + extractedOverridden.none { overridden2 -> + overridden1 !== overridden2 && overrides( + overridden2, + overridden1, + processAllOverridden + ) + } + } + } + + // Whether f overrides g + private fun > overrides( + f: MemberWithBaseScope, + g: MemberWithBaseScope, + processAllOverridden: ProcessOverriddenWithBaseScope, + ): 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 > chooseIntersectionVisibility( + extractedOverrides: Collection> + ): 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>, + 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>, + 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>(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.withScope(baseScope: FirTypeScope) = MemberWithBaseScope(this, baseScope) + +class FirIntersectionOverrideStorage(val session: FirSession) : FirSessionComponent { + private val cachesFactory = session.firCachesFactory + + class CacheForScope(cachesFactory: FirCachesFactory) { + val intersectionOverrides: FirCache, MemberWithBaseScope>, ContextForIntersectionOverrideConstruction<*>> = + cachesFactory.createCache { mostSpecific, context -> + val (intersectionScope, extractedOverrides, scopeForMostSpecific) = context + intersectionScope.createIntersectionOverride(extractedOverrides, mostSpecific, scopeForMostSpecific) + } + } + + data class ContextForIntersectionOverrideConstruction>( + val intersectionContext: FirTypeIntersectionScopeContext, + val extractedOverrides: List>, + val scopeForMostSpecific: FirTypeScope + ) + + val cacheByScope: FirCache = + cachesFactory.createCache { _ -> CacheForScope(cachesFactory) } +} + +private val FirSession.intersectionOverrideStorage: FirIntersectionOverrideStorage by FirSession.sessionComponentAccessor()