[FIR] Don't remove subsumed members from intersection overrides's overriddens

This fixes a bunch of missing overridden symbols in IR.
This is also required for fixing KT-59921 in the following commit
where we need to keep all overridden symbols of intersection overrides
so that we can enhance them properly.

#KT-57300 Fixed
#KT-57299 Fixed
#KT-59921
#KT-57300
#KT-62788
#KT-64271
#KT-64382
This commit is contained in:
Kirill Rakhman
2024-01-17 09:59:17 +01:00
committed by Space Team
parent d80dee6e1c
commit 3b841dcb98
58 changed files with 899 additions and 530 deletions
@@ -9,7 +9,10 @@ import org.jetbrains.kotlin.descriptors.ClassKind
import org.jetbrains.kotlin.fir.FirSession
import org.jetbrains.kotlin.util.PrivateForInline
import org.jetbrains.kotlin.fir.declarations.utils.isInline
import org.jetbrains.kotlin.fir.resolve.ScopeSession
import org.jetbrains.kotlin.fir.resolve.fullyExpandedType
import org.jetbrains.kotlin.fir.resolve.scope
import org.jetbrains.kotlin.fir.scopes.*
import org.jetbrains.kotlin.fir.scopes.impl.declaredMemberScope
import org.jetbrains.kotlin.fir.scopes.impl.importedFromObjectOrStaticData
import org.jetbrains.kotlin.fir.symbols.FirBasedSymbol
@@ -19,6 +22,7 @@ import org.jetbrains.kotlin.fir.types.coneType
import org.jetbrains.kotlin.fir.types.coneTypeSafe
import org.jetbrains.kotlin.fir.types.isNullableAny
import org.jetbrains.kotlin.fir.types.toSymbol
import org.jetbrains.kotlin.fir.unwrapSubstitutionOverrides
import org.jetbrains.kotlin.util.OperatorNameConventions
fun FirClass.constructors(session: FirSession): List<FirConstructorSymbol> {
@@ -126,3 +130,72 @@ fun FirSimpleFunction.isEquals(session: FirSession): Boolean {
val parameter = valueParameters.first()
return parameter.returnTypeRef.coneType.fullyExpandedType(session).isNullableAny
}
/**
* An intersection override is trivial if one of the overridden symbols subsumes all others.
*
* @see org.jetbrains.kotlin.fir.scopes.impl.FirTypeIntersectionScopeContext.convertGroupedCallablesToIntersectionResults
*/
fun MemberWithBaseScope<FirCallableSymbol<*>>.isTrivialIntersection(): Boolean {
return baseScope
.getDirectOverriddenMembersWithBaseScope(member)
.nonSubsumed()
.mapTo(mutableSetOf()) { it.member.unwrapSubstitutionOverrides() }.size == 1
}
fun FirIntersectionCallableSymbol.getNonSubsumedOverriddenSymbols(
session: FirSession,
scopeSession: ScopeSession,
): List<FirCallableSymbol<*>> {
require(this is FirCallableSymbol<*>)
val dispatchReceiverScope = dispatchReceiverScope(session, scopeSession)
return MemberWithBaseScope(this, dispatchReceiverScope).getNonSubsumedOverriddenSymbols()
}
fun MemberWithBaseScope<FirCallableSymbol<*>>.getNonSubsumedOverriddenSymbols(): List<FirCallableSymbol<*>> {
return flattenIntersectionsRecursively()
.nonSubsumed()
.distinctBy { it.member.unwrapSubstitutionOverrides<FirCallableSymbol<*>>() }
.map { it.member }
}
fun FirCallableSymbol<*>.dispatchReceiverScope(session: FirSession, scopeSession: ScopeSession): FirTypeScope {
val dispatchReceiverType = requireNotNull(dispatchReceiverType)
return dispatchReceiverType.scope(
session,
scopeSession,
CallableCopyTypeCalculator.DoNothing,
FirResolvePhase.STATUS
) ?: FirTypeScope.Empty
}
fun MemberWithBaseScope<FirCallableSymbol<*>>.flattenIntersectionsRecursively(): List<MemberWithBaseScope<FirCallableSymbol<*>>> {
if (member.origin != FirDeclarationOrigin.IntersectionOverride) return listOf(this)
return baseScope.getDirectOverriddenMembersWithBaseScope(member).flatMap { it.flattenIntersectionsRecursively() }
}
/**
* A callable declaration D [subsumes](https://kotlinlang.org/spec/inheritance.html#matching-and-subsumption-of-declarations)
* a callable declaration B if D overrides B.
*/
fun Collection<MemberWithBaseScope<FirCallableSymbol<*>>>.nonSubsumed(): List<MemberWithBaseScope<FirCallableSymbol<*>>> {
val baseMembers = mutableSetOf<FirCallableSymbol<*>>()
for ((member, scope) in this) {
val unwrapped = member.unwrapSubstitutionOverrides<FirCallableSymbol<*>>()
val addIfDifferent = { it: FirCallableSymbol<*> ->
val symbol = it.unwrapSubstitutionOverrides()
if (symbol != unwrapped) {
baseMembers += symbol
}
ProcessorAction.NEXT
}
if (member is FirNamedFunctionSymbol) {
scope.processOverriddenFunctions(member, addIfDifferent)
} else if (member is FirPropertySymbol) {
scope.processOverriddenProperties(member, addIfDifferent)
}
}
return filter { (member, _) -> member.unwrapSubstitutionOverrides<FirCallableSymbol<*>>() !in baseMembers }
}
@@ -10,9 +10,7 @@ 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.FirDeclarationOrigin
import org.jetbrains.kotlin.fir.declarations.FirMemberDeclaration
import org.jetbrains.kotlin.fir.declarations.FirVariable
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
@@ -159,8 +157,7 @@ class FirTypeIntersectionScopeContext(
val groupWithInvisible =
overrideService.extractBothWaysOverridable(allMembersWithScope.maxByVisibility(), allMembersWithScope, overrideChecker)
val group = groupWithInvisible.filter { it.isVisible() }.ifEmpty { groupWithInvisible }
val nonSubsumed = if (forClassUseSiteScope) group.nonSubsumed() else group
val mostSpecific = overrideService.selectMostSpecificMembers(nonSubsumed, ReturnTypeCalculatorForFullBodyResolve.Default)
val mostSpecific = overrideService.selectMostSpecificMembers(group, ReturnTypeCalculatorForFullBodyResolve.Default)
val nonTrivial = if (forClassUseSiteScope) {
// Create a non-trivial intersection override when the base methods come from different scopes,
// even if one of them is more specific than the others, i.e. when there is more than one method that is not subsumed.
@@ -169,15 +166,15 @@ class FirTypeIntersectionScopeContext(
// It is also possible to have the opposite case (> 1 most specific member, but all members are from
// the same base scope), but this means there are different instantiations of the same base class,
// which should generally result in INCONSISTENT_TYPE_PARAMETER_VALUES errors.
nonSubsumed.size > 1 &&
nonSubsumed.mapTo(mutableSetOf()) { it.member.fir.unwrapSubstitutionOverrides().symbol }.size > 1
group.size > 1 &&
group.mapTo(mutableSetOf()) { it.member.fir.unwrapSubstitutionOverrides().symbol }.size > 1
} else {
// Create a non-trivial intersection override when return types should be intersected.
mostSpecific.size > 1
}
if (nonTrivial) {
// Only add non-subsumed members to list of overridden in intersection override.
result += ResultOfIntersection.NonTrivial(this, mostSpecific, overriddenMembers = nonSubsumed, containingScope = null)
result += ResultOfIntersection.NonTrivial(this, mostSpecific, overriddenMembers = group, containingScope = null)
} else {
val (member, containingScope) = mostSpecific.first()
result += ResultOfIntersection.SingleMember(member, group, containingScope)
@@ -207,7 +204,7 @@ class FirTypeIntersectionScopeContext(
mostSpecific: List<MemberWithBaseScope<D>>,
extractedOverrides: List<MemberWithBaseScope<D>>,
): MemberWithBaseScope<FirCallableSymbol<*>> {
val newModality = chooseIntersectionOverrideModality(extractedOverrides)
val newModality = chooseIntersectionOverrideModality(extractedOverrides.flatMap { it.flattenIntersectionsRecursively() }.nonSubsumed())
val newVisibility = chooseIntersectionVisibility(extractedOverrides)
val mostSpecificSymbols = mostSpecific.map { it.member }
val extractedOverridesSymbols = extractedOverrides.map { it.member }
@@ -228,30 +225,6 @@ class FirTypeIntersectionScopeContext(
}.withScope(key.baseScope)
}
/**
* A callable declaration D [subsumes](https://kotlinlang.org/spec/inheritance.html#matching-and-subsumption-of-declarations)
* a callable declaration B if D overrides B.
*/
private fun <S : FirCallableSymbol<*>> List<MemberWithBaseScope<S>>.nonSubsumed(): List<MemberWithBaseScope<S>> {
val baseMembers = mutableSetOf<FirCallableSymbol<*>>()
for ((member, scope) in this) {
val unwrapped = member.unwrapSubstitutionOverrides<FirCallableSymbol<*>>()
val addIfDifferent = { it: FirCallableSymbol<*> ->
val symbol = it.unwrapSubstitutionOverrides()
if (symbol != unwrapped) {
baseMembers += symbol
}
ProcessorAction.NEXT
}
if (member is FirNamedFunctionSymbol) {
scope.processOverriddenFunctions(member, addIfDifferent)
} else if (member is FirPropertySymbol) {
scope.processOverriddenProperties(member, addIfDifferent)
}
}
return filter { it.member.unwrapSubstitutionOverrides<FirCallableSymbol<*>>() !in baseMembers }
}
private fun <S : FirCallableSymbol<*>> Collection<MemberWithBaseScope<S>>.maxByVisibility(): MemberWithBaseScope<S> {
var member: MemberWithBaseScope<S>? = null
for (candidate in this) {