From caffe361814a977f44df17751c06055578fe2fae Mon Sep 17 00:00:00 2001 From: Dmitriy Novozhilov Date: Mon, 14 Mar 2022 17:47:17 +0300 Subject: [PATCH] [FIR] Move utilities for matching overrides into separate session component --- .../fir/session/ComponentsContainers.kt | 2 + .../kotlin/fir/scopes/FirOverrideService.kt | 157 ++++++++++++++++++ .../impl/FirTypeIntersectionScopeContext.kt | 103 +----------- 3 files changed, 164 insertions(+), 98 deletions(-) create mode 100644 compiler/fir/providers/src/org/jetbrains/kotlin/fir/scopes/FirOverrideService.kt diff --git a/compiler/fir/entrypoint/src/org/jetbrains/kotlin/fir/session/ComponentsContainers.kt b/compiler/fir/entrypoint/src/org/jetbrains/kotlin/fir/session/ComponentsContainers.kt index 3af9bc04bc9..8e24ae821cc 100644 --- a/compiler/fir/entrypoint/src/org/jetbrains/kotlin/fir/session/ComponentsContainers.kt +++ b/compiler/fir/entrypoint/src/org/jetbrains/kotlin/fir/session/ComponentsContainers.kt @@ -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) diff --git a/compiler/fir/providers/src/org/jetbrains/kotlin/fir/scopes/FirOverrideService.kt b/compiler/fir/providers/src/org/jetbrains/kotlin/fir/scopes/FirOverrideService.kt new file mode 100644 index 00000000000..e843e1e7cfd --- /dev/null +++ b/compiler/fir/providers/src/org/jetbrains/kotlin/fir/scopes/FirOverrideService.kt @@ -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 > selectMostSpecificInEachOverridableGroup( + members: Collection>, + overrideChecker: FirOverrideChecker + ): Collection> { + if (members.size <= 1) return members + val queue = LinkedList(members) + val result = SmartSet.create>() + + while (queue.isNotEmpty()) { + val nextHandle = queue.first() + + val conflictedHandles = SmartSet.create>() + + 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 > extractBothWaysOverridable( + overrider: MemberWithBaseScope, + members: MutableCollection>, + overrideChecker: FirOverrideChecker + ): 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 + } + + 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 + + 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() 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 index 5e4a108c8d8..80712b62aa0 100644 --- 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 @@ -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, private val dispatchReceiverType: ConeSimpleKotlinType, ) { - private val typeCheckerState = session.typeContext.newTypeCheckerState( - errorTypesEqualToAnything = false, - stubTypesEqualToAnything = false - ) + private val overrideService = session.overrideService val intersectionOverrides: FirCache, MemberWithBaseScope>, 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() @@ -244,76 +240,12 @@ class FirTypeIntersectionScopeContext( } } } + 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()) @@ -335,31 +267,6 @@ class FirTypeIntersectionScopeContext( 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? {