[FIR] Make MemberWithScope a data class

Previously, there was a contract that each callable symbol in the chain
  of `processDirectOverriddenWithBaseScope` will be unique. And if some
  symbol is accessible from multiple scopes, then only last of them will
  be returned as a component of `MemberWithScope`. So there actually was
  no such thing as "pair of two different MemberWithScope with the same
  symbol and different scopes"

After the change of `processDirectOverriddenWithBaseScope` contract (see
  previous commits) each scope returns `MemberWithScope` for some symbol
  with the previous scope in the hierarchy even if it contains the same
  symbol as the current scope

So now scope is actually a part of `MemberWithScope`, which should be
  considered as part of equality. Otherwise, we can skip some part of
  the overridden hierarchy, because we will start to consider symbol as
  visited after its first occurrence (in opposite to the previous behavior,
  when only the last scope was returned)
This commit is contained in:
Dmitriy Novozhilov
2023-12-05 13:24:06 +02:00
committed by Space Team
parent 385bff3be1
commit bc95c5d142
4 changed files with 31 additions and 23 deletions
@@ -30,4 +30,4 @@ fun test_1(x: String?) {
fun test_2(x: String?) { fun test_2(x: String?) {
trickyRequireNotNull(x) trickyRequireNotNull(x)
x.length x.length
} }
@@ -144,8 +144,13 @@ class FirTypeIntersectionScopeContext(
return members.map { ResultOfIntersection.SingleMember(it, MemberWithBaseScope(it, scope)) } return members.map { ResultOfIntersection.SingleMember(it, MemberWithBaseScope(it, scope)) }
} }
val uniqueSymbols = mutableSetOf<D>()
val allMembersWithScope = membersByScope.flatMapTo(linkedSetOf()) { (scope, members) -> val allMembersWithScope = membersByScope.flatMapTo(linkedSetOf()) { (scope, members) ->
members.map { MemberWithBaseScope(it, scope) } members.mapNotNull {
runIf(uniqueSymbols.add(it)) {
MemberWithBaseScope(it, scope)
}
}
} }
val result = mutableListOf<ResultOfIntersection<D>>() val result = mutableListOf<ResultOfIntersection<D>>()
@@ -92,26 +92,30 @@ class MemberScopeTowerLevel(
) )
if (scopeWithoutSmartcast == null) { if (scopeWithoutSmartcast == null) {
consumeCandidates(output, candidates) consumeCandidates(
output,
candidatesWithoutSmartcast = candidates,
candidatesWithSmartcast = null
)
} else { } else {
val isFromSmartCast: MutableMap<MemberWithBaseScope<T>, Boolean> = mutableMapOf() val map: MutableMap<T, MemberFromSmartcastScope<T>> = mutableMapOf()
scopeWithoutSmartcast.collectCandidates(processScopeMembers).let { (isEmpty, originalCandidates) -> scopeWithoutSmartcast.collectCandidates(processScopeMembers).let { (isEmpty, originalCandidates) ->
empty = empty && isEmpty empty = empty && isEmpty
for (originalCandidate in originalCandidates) { for (originalCandidate in originalCandidates) {
isFromSmartCast[originalCandidate] = false map[originalCandidate.member] = MemberFromSmartcastScope(originalCandidate, cameFromSmartcast = false)
} }
} }
for (candidateFromSmartCast in candidates) { for (candidateFromSmartCast in candidates) {
isFromSmartCast[candidateFromSmartCast] = true map[candidateFromSmartCast.member] = MemberFromSmartcastScope(candidateFromSmartCast, cameFromSmartcast = true)
} }
consumeCandidates( consumeCandidates(
output, output,
// all the candidates, both from original type and smart cast // all the candidates, both from original type and smart cast
candidates = isFromSmartCast.keys, candidatesWithoutSmartcast = null,
isFromSmartCast, candidatesWithSmartcast = map
) )
} }
@@ -162,6 +166,11 @@ class MemberScopeTowerLevel(
return if (empty) ProcessResult.SCOPE_EMPTY else ProcessResult.FOUND return if (empty) ProcessResult.SCOPE_EMPTY else ProcessResult.FOUND
} }
private data class MemberFromSmartcastScope<T : FirCallableSymbol<*>>(
val memberWithBaseScope: MemberWithBaseScope<T>,
val cameFromSmartcast: Boolean
)
private fun <T : FirCallableSymbol<*>> FirTypeScope.collectCandidates( private fun <T : FirCallableSymbol<*>> FirTypeScope.collectCandidates(
processScopeMembers: FirScope.(processor: (T) -> Unit) -> Unit processScopeMembers: FirScope.(processor: (T) -> Unit) -> Unit
): Pair<Boolean, List<MemberWithBaseScope<T>>> { ): Pair<Boolean, List<MemberWithBaseScope<T>>> {
@@ -185,16 +194,21 @@ class MemberScopeTowerLevel(
private fun <T : FirCallableSymbol<*>> consumeCandidates( private fun <T : FirCallableSymbol<*>> consumeCandidates(
output: TowerScopeLevelProcessor<T>, output: TowerScopeLevelProcessor<T>,
candidates: Collection<MemberWithBaseScope<T>>, candidatesWithoutSmartcast: Collection<MemberWithBaseScope<T>>?,
// The map is not null only if there's a smart cast type on a dispatch receiver // The map is not null only if there's a smart cast type on a dispatch receiver
// and candidates are present both in smart cast and original types. // and candidates are present both in smart cast and original types.
// isFromSmartCast[candidate] == true iff exactly that member is present in smart cast type // isFromSmartCast[candidate] == true iff exactly that member is present in smart cast type
isFromSmartCast: Map<MemberWithBaseScope<T>, Boolean>? = null candidatesWithSmartcast: Map<T, MemberFromSmartcastScope<T>>?
) { ) {
val candidates = candidatesWithoutSmartcast
?: candidatesWithSmartcast?.values?.map { it.memberWithBaseScope }
?: error("candidatesWithoutSmartcast or candidatesWithSmartcast should be not null")
for (candidateWithScope in candidates) { for (candidateWithScope in candidates) {
val (candidate, scope) = candidateWithScope val (candidate, scope) = candidateWithScope
if (candidate.hasConsistentExtensionReceiver(givenExtensionReceiverOptions)) { if (candidate.hasConsistentExtensionReceiver(givenExtensionReceiverOptions)) {
val isFromOriginalTypeInPresenceOfSmartCast = isFromSmartCast != null && !isFromSmartCast.getValue(candidateWithScope) val isFromOriginalTypeInPresenceOfSmartCast = candidatesWithSmartcast != null &&
!candidatesWithSmartcast.getValue(candidateWithScope.member).cameFromSmartcast
val dispatchReceiverToUse = when { val dispatchReceiverToUse = when {
isFromOriginalTypeInPresenceOfSmartCast -> isFromOriginalTypeInPresenceOfSmartCast ->
@@ -64,18 +64,7 @@ abstract class FirTypeScope : FirContainingNamesAwareScope() {
} }
} }
class MemberWithBaseScope<out D : FirCallableSymbol<*>>(val member: D, val baseScope: FirTypeScope) { data class MemberWithBaseScope<out D : FirCallableSymbol<*>>(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()
}
}
typealias ProcessOverriddenWithBaseScope<D> = FirTypeScope.(D, (D, FirTypeScope) -> ProcessorAction) -> ProcessorAction typealias ProcessOverriddenWithBaseScope<D> = FirTypeScope.(D, (D, FirTypeScope) -> ProcessorAction) -> ProcessorAction
typealias ProcessAllOverridden<D> = FirTypeScope.(D, (D) -> ProcessorAction) -> ProcessorAction typealias ProcessAllOverridden<D> = FirTypeScope.(D, (D) -> ProcessorAction) -> ProcessorAction