[FIR] Move utilities for matching overrides into separate session component

This commit is contained in:
Dmitriy Novozhilov
2022-03-14 17:47:17 +03:00
committed by teamcity
parent 0025d170d1
commit caffe36181
3 changed files with 164 additions and 98 deletions
@@ -35,6 +35,7 @@ import org.jetbrains.kotlin.fir.resolve.providers.impl.FirQualifierResolverImpl
import org.jetbrains.kotlin.fir.resolve.providers.impl.FirTypeResolverImpl
import org.jetbrains.kotlin.fir.resolve.transformers.FirPhaseCheckingPhaseManager
import org.jetbrains.kotlin.fir.resolve.transformers.plugin.GeneratedClassIndex
import org.jetbrains.kotlin.fir.scopes.FirOverrideService
import org.jetbrains.kotlin.fir.scopes.FirPlatformClassMapper
import org.jetbrains.kotlin.fir.scopes.impl.FirDeclaredMemberScopeProvider
import org.jetbrains.kotlin.fir.scopes.impl.FirIntersectionOverrideStorage
@@ -63,6 +64,7 @@ fun FirSession.registerCommonComponents(languageVersionSettings: LanguageVersion
register(FirSubstitutionOverrideStorage::class, FirSubstitutionOverrideStorage(this))
register(FirIntersectionOverrideStorage::class, FirIntersectionOverrideStorage(this))
register(FirSamConstructorStorage::class, FirSamConstructorStorage(this))
register(FirOverrideService::class, FirOverrideService(this))
}
@OptIn(SessionConfiguration::class)
@@ -0,0 +1,157 @@
/*
* Copyright 2010-2022 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
import org.jetbrains.kotlin.fir.FirSession
import org.jetbrains.kotlin.fir.FirSessionComponent
import org.jetbrains.kotlin.fir.declarations.FirProperty
import org.jetbrains.kotlin.fir.declarations.FirSimpleFunction
import org.jetbrains.kotlin.fir.scopes.impl.buildSubstitutorForOverridesCheck
import org.jetbrains.kotlin.fir.scopes.impl.similarFunctionsOrBothProperties
import org.jetbrains.kotlin.fir.symbols.impl.FirCallableSymbol
import org.jetbrains.kotlin.fir.types.ConeFlexibleType
import org.jetbrains.kotlin.fir.types.ConeKotlinType
import org.jetbrains.kotlin.fir.types.coneTypeSafe
import org.jetbrains.kotlin.fir.types.typeContext
import org.jetbrains.kotlin.types.AbstractTypeChecker
import org.jetbrains.kotlin.types.TypeCheckerState
import org.jetbrains.kotlin.utils.SmartSet
import java.util.*
class FirOverrideService(val session: FirSession) : FirSessionComponent {
fun <D : FirCallableSymbol<*>> selectMostSpecificInEachOverridableGroup(
members: Collection<MemberWithBaseScope<D>>,
overrideChecker: FirOverrideChecker
): Collection<MemberWithBaseScope<D>> {
if (members.size <= 1) return members
val queue = LinkedList(members)
val result = SmartSet.create<MemberWithBaseScope<D>>()
while (queue.isNotEmpty()) {
val nextHandle = queue.first()
val conflictedHandles = SmartSet.create<MemberWithBaseScope<D>>()
val overridableGroup = extractBothWaysOverridable(nextHandle, queue, overrideChecker)
if (overridableGroup.size == 1 && conflictedHandles.isEmpty()) {
result.add(overridableGroup.single())
continue
}
val mostSpecific = selectMostSpecificMember(overridableGroup)
overridableGroup.filterNotTo(conflictedHandles) {
isMoreSpecific(mostSpecific.member, it.member)
}
if (conflictedHandles.isNotEmpty()) {
result.addAll(conflictedHandles)
}
result.add(mostSpecific)
}
return result
}
fun <D : FirCallableSymbol<*>> extractBothWaysOverridable(
overrider: MemberWithBaseScope<D>,
members: MutableCollection<MemberWithBaseScope<D>>,
overrideChecker: FirOverrideChecker
): 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
}
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
val typeCheckerState = session.typeContext.newTypeCheckerState(
errorTypesEqualToAnything = false,
stubTypesEqualToAnything = false
)
if (aFir is FirSimpleFunction) {
require(bFir is FirSimpleFunction) { "b is " + b.javaClass }
return isTypeMoreSpecific(aReturnType, bReturnType, typeCheckerState)
}
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, typeCheckerState)
}
}
throw IllegalArgumentException("Unexpected callable: " + a.javaClass)
}
private fun isTypeMoreSpecific(a: ConeKotlinType, b: ConeKotlinType, typeCheckerState: TypeCheckerState): Boolean =
AbstractTypeChecker.isSubtypeOf(typeCheckerState, a, b)
}
val FirSession.overrideService: FirOverrideService by FirSession.sessionComponentAccessor()
@@ -22,7 +22,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.types.AbstractTypeChecker
import kotlin.contracts.ExperimentalContracts
import kotlin.contracts.contract
@@ -34,10 +33,7 @@ class FirTypeIntersectionScopeContext(
val scopes: List<FirTypeScope>,
private val dispatchReceiverType: ConeSimpleKotlinType,
) {
private val typeCheckerState = session.typeContext.newTypeCheckerState(
errorTypesEqualToAnything = false,
stubTypesEqualToAnything = false
)
private val overrideService = session.overrideService
val intersectionOverrides: FirCache<FirCallableSymbol<*>, MemberWithBaseScope<FirCallableSymbol<*>>, ContextForIntersectionOverrideConstruction<*>> =
session.intersectionOverrideStorage.cacheByScope.getValue(dispatchReceiverType).intersectionOverrides
@@ -161,13 +157,13 @@ class FirTypeIntersectionScopeContext(
while (allMembersWithScope.size > 1) {
val maxByVisibility = findMemberWithMaxVisibility(allMembersWithScope)
val extractBothWaysWithPrivate = extractBothWaysOverridable(maxByVisibility, allMembersWithScope)
val extractBothWaysWithPrivate = overrideService.extractBothWaysOverridable(maxByVisibility, allMembersWithScope, overrideChecker)
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 (mostSpecific, scopeForMostSpecific) = overrideService.selectMostSpecificMember(baseMembersForIntersection)
val intersectionOverrideContext = ContextForIntersectionOverrideConstruction(
mostSpecific,
this,
@@ -220,7 +216,7 @@ class FirTypeIntersectionScopeContext(
// 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))
return listOf(overrideService.selectMostSpecificMember(this))
}
val baseMembers = mutableSetOf<S>()
@@ -244,76 +240,12 @@ class FirTypeIntersectionScopeContext(
}
}
}
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())
@@ -335,31 +267,6 @@ class FirTypeIntersectionScopeContext(
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? {