[FIR] Move utilities for matching overrides into separate session component
This commit is contained in:
committed by
teamcity
parent
0025d170d1
commit
caffe36181
@@ -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.providers.impl.FirTypeResolverImpl
|
||||||
import org.jetbrains.kotlin.fir.resolve.transformers.FirPhaseCheckingPhaseManager
|
import org.jetbrains.kotlin.fir.resolve.transformers.FirPhaseCheckingPhaseManager
|
||||||
import org.jetbrains.kotlin.fir.resolve.transformers.plugin.GeneratedClassIndex
|
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.FirPlatformClassMapper
|
||||||
import org.jetbrains.kotlin.fir.scopes.impl.FirDeclaredMemberScopeProvider
|
import org.jetbrains.kotlin.fir.scopes.impl.FirDeclaredMemberScopeProvider
|
||||||
import org.jetbrains.kotlin.fir.scopes.impl.FirIntersectionOverrideStorage
|
import org.jetbrains.kotlin.fir.scopes.impl.FirIntersectionOverrideStorage
|
||||||
@@ -63,6 +64,7 @@ fun FirSession.registerCommonComponents(languageVersionSettings: LanguageVersion
|
|||||||
register(FirSubstitutionOverrideStorage::class, FirSubstitutionOverrideStorage(this))
|
register(FirSubstitutionOverrideStorage::class, FirSubstitutionOverrideStorage(this))
|
||||||
register(FirIntersectionOverrideStorage::class, FirIntersectionOverrideStorage(this))
|
register(FirIntersectionOverrideStorage::class, FirIntersectionOverrideStorage(this))
|
||||||
register(FirSamConstructorStorage::class, FirSamConstructorStorage(this))
|
register(FirSamConstructorStorage::class, FirSamConstructorStorage(this))
|
||||||
|
register(FirOverrideService::class, FirOverrideService(this))
|
||||||
}
|
}
|
||||||
|
|
||||||
@OptIn(SessionConfiguration::class)
|
@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()
|
||||||
+5
-98
@@ -22,7 +22,6 @@ import org.jetbrains.kotlin.fir.symbols.impl.*
|
|||||||
import org.jetbrains.kotlin.fir.types.*
|
import org.jetbrains.kotlin.fir.types.*
|
||||||
import org.jetbrains.kotlin.name.CallableId
|
import org.jetbrains.kotlin.name.CallableId
|
||||||
import org.jetbrains.kotlin.name.Name
|
import org.jetbrains.kotlin.name.Name
|
||||||
import org.jetbrains.kotlin.types.AbstractTypeChecker
|
|
||||||
import kotlin.contracts.ExperimentalContracts
|
import kotlin.contracts.ExperimentalContracts
|
||||||
import kotlin.contracts.contract
|
import kotlin.contracts.contract
|
||||||
|
|
||||||
@@ -34,10 +33,7 @@ class FirTypeIntersectionScopeContext(
|
|||||||
val scopes: List<FirTypeScope>,
|
val scopes: List<FirTypeScope>,
|
||||||
private val dispatchReceiverType: ConeSimpleKotlinType,
|
private val dispatchReceiverType: ConeSimpleKotlinType,
|
||||||
) {
|
) {
|
||||||
private val typeCheckerState = session.typeContext.newTypeCheckerState(
|
private val overrideService = session.overrideService
|
||||||
errorTypesEqualToAnything = false,
|
|
||||||
stubTypesEqualToAnything = false
|
|
||||||
)
|
|
||||||
|
|
||||||
val intersectionOverrides: FirCache<FirCallableSymbol<*>, MemberWithBaseScope<FirCallableSymbol<*>>, ContextForIntersectionOverrideConstruction<*>> =
|
val intersectionOverrides: FirCache<FirCallableSymbol<*>, MemberWithBaseScope<FirCallableSymbol<*>>, ContextForIntersectionOverrideConstruction<*>> =
|
||||||
session.intersectionOverrideStorage.cacheByScope.getValue(dispatchReceiverType).intersectionOverrides
|
session.intersectionOverrideStorage.cacheByScope.getValue(dispatchReceiverType).intersectionOverrides
|
||||||
@@ -161,13 +157,13 @@ class FirTypeIntersectionScopeContext(
|
|||||||
|
|
||||||
while (allMembersWithScope.size > 1) {
|
while (allMembersWithScope.size > 1) {
|
||||||
val maxByVisibility = findMemberWithMaxVisibility(allMembersWithScope)
|
val maxByVisibility = findMemberWithMaxVisibility(allMembersWithScope)
|
||||||
val extractBothWaysWithPrivate = extractBothWaysOverridable(maxByVisibility, allMembersWithScope)
|
val extractBothWaysWithPrivate = overrideService.extractBothWaysOverridable(maxByVisibility, allMembersWithScope, overrideChecker)
|
||||||
val extractedOverrides = extractBothWaysWithPrivate.filterNotTo(mutableListOf()) {
|
val extractedOverrides = extractBothWaysWithPrivate.filterNotTo(mutableListOf()) {
|
||||||
Visibilities.isPrivate((it.member.fir as FirMemberDeclaration).visibility)
|
Visibilities.isPrivate((it.member.fir as FirMemberDeclaration).visibility)
|
||||||
}.takeIf { it.isNotEmpty() } ?: extractBothWaysWithPrivate
|
}.takeIf { it.isNotEmpty() } ?: extractBothWaysWithPrivate
|
||||||
val baseMembersForIntersection = extractedOverrides.calcBaseMembersForIntersectionOverride()
|
val baseMembersForIntersection = extractedOverrides.calcBaseMembersForIntersectionOverride()
|
||||||
if (baseMembersForIntersection.size > 1) {
|
if (baseMembersForIntersection.size > 1) {
|
||||||
val (mostSpecific, scopeForMostSpecific) = selectMostSpecificMember(baseMembersForIntersection)
|
val (mostSpecific, scopeForMostSpecific) = overrideService.selectMostSpecificMember(baseMembersForIntersection)
|
||||||
val intersectionOverrideContext = ContextForIntersectionOverrideConstruction(
|
val intersectionOverrideContext = ContextForIntersectionOverrideConstruction(
|
||||||
mostSpecific,
|
mostSpecific,
|
||||||
this,
|
this,
|
||||||
@@ -220,7 +216,7 @@ class FirTypeIntersectionScopeContext(
|
|||||||
// we should just take most specific member without creating intersection
|
// 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
|
// A typical sample here is inheritance of the same class in different places of hierarchy
|
||||||
if (unwrappedMemberSet.size == 1) {
|
if (unwrappedMemberSet.size == 1) {
|
||||||
return listOf(selectMostSpecificMember(this))
|
return listOf(overrideService.selectMostSpecificMember(this))
|
||||||
}
|
}
|
||||||
|
|
||||||
val baseMembers = mutableSetOf<S>()
|
val baseMembers = mutableSetOf<S>()
|
||||||
@@ -244,76 +240,12 @@ class FirTypeIntersectionScopeContext(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val result = this.toMutableList()
|
val result = this.toMutableList()
|
||||||
result.removeIf { (member, _) -> member.fir.unwrapSubstitutionOverrides().symbol in baseMembers }
|
result.removeIf { (member, _) -> member.fir.unwrapSubstitutionOverrides().symbol in baseMembers }
|
||||||
return result
|
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> {
|
private fun <D : FirCallableSymbol<*>> findMemberWithMaxVisibility(members: Collection<MemberWithBaseScope<D>>): MemberWithBaseScope<D> {
|
||||||
assert(members.isNotEmpty())
|
assert(members.isNotEmpty())
|
||||||
|
|
||||||
@@ -335,31 +267,6 @@ class FirTypeIntersectionScopeContext(
|
|||||||
return member!!
|
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(
|
private fun <D : FirCallableSymbol<*>> chooseIntersectionOverrideModality(
|
||||||
extractedOverridden: Collection<MemberWithBaseScope<D>>
|
extractedOverridden: Collection<MemberWithBaseScope<D>>
|
||||||
): Modality? {
|
): Modality? {
|
||||||
|
|||||||
Reference in New Issue
Block a user