diff --git a/compiler/fir/analysis-tests/testData/resolveWithStdlib/contracts/fromSource/good/returnsImplies/eqNotEq.kt b/compiler/fir/analysis-tests/testData/resolveWithStdlib/contracts/fromSource/good/returnsImplies/eqNotEq.kt index f7e7bd2d086..22c07365f15 100644 --- a/compiler/fir/analysis-tests/testData/resolveWithStdlib/contracts/fromSource/good/returnsImplies/eqNotEq.kt +++ b/compiler/fir/analysis-tests/testData/resolveWithStdlib/contracts/fromSource/good/returnsImplies/eqNotEq.kt @@ -30,4 +30,4 @@ fun test_1(x: String?) { fun test_2(x: String?) { trickyRequireNotNull(x) x.length -} \ No newline at end of file +} 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 846432205c1..b2e3afb21ea 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 @@ -144,8 +144,13 @@ class FirTypeIntersectionScopeContext( return members.map { ResultOfIntersection.SingleMember(it, MemberWithBaseScope(it, scope)) } } + val uniqueSymbols = mutableSetOf() val allMembersWithScope = membersByScope.flatMapTo(linkedSetOf()) { (scope, members) -> - members.map { MemberWithBaseScope(it, scope) } + members.mapNotNull { + runIf(uniqueSymbols.add(it)) { + MemberWithBaseScope(it, scope) + } + } } val result = mutableListOf>() diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/tower/TowerLevels.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/tower/TowerLevels.kt index d7496ad4cb5..6206b57e272 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/tower/TowerLevels.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/tower/TowerLevels.kt @@ -92,26 +92,30 @@ class MemberScopeTowerLevel( ) if (scopeWithoutSmartcast == null) { - consumeCandidates(output, candidates) + consumeCandidates( + output, + candidatesWithoutSmartcast = candidates, + candidatesWithSmartcast = null + ) } else { - val isFromSmartCast: MutableMap, Boolean> = mutableMapOf() + val map: MutableMap> = mutableMapOf() scopeWithoutSmartcast.collectCandidates(processScopeMembers).let { (isEmpty, originalCandidates) -> empty = empty && isEmpty for (originalCandidate in originalCandidates) { - isFromSmartCast[originalCandidate] = false + map[originalCandidate.member] = MemberFromSmartcastScope(originalCandidate, cameFromSmartcast = false) } } for (candidateFromSmartCast in candidates) { - isFromSmartCast[candidateFromSmartCast] = true + map[candidateFromSmartCast.member] = MemberFromSmartcastScope(candidateFromSmartCast, cameFromSmartcast = true) } consumeCandidates( output, // all the candidates, both from original type and smart cast - candidates = isFromSmartCast.keys, - isFromSmartCast, + candidatesWithoutSmartcast = null, + candidatesWithSmartcast = map ) } @@ -162,6 +166,11 @@ class MemberScopeTowerLevel( return if (empty) ProcessResult.SCOPE_EMPTY else ProcessResult.FOUND } + private data class MemberFromSmartcastScope>( + val memberWithBaseScope: MemberWithBaseScope, + val cameFromSmartcast: Boolean + ) + private fun > FirTypeScope.collectCandidates( processScopeMembers: FirScope.(processor: (T) -> Unit) -> Unit ): Pair>> { @@ -185,16 +194,21 @@ class MemberScopeTowerLevel( private fun > consumeCandidates( output: TowerScopeLevelProcessor, - candidates: Collection>, + candidatesWithoutSmartcast: Collection>?, // 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. // isFromSmartCast[candidate] == true iff exactly that member is present in smart cast type - isFromSmartCast: Map, Boolean>? = null + candidatesWithSmartcast: Map>? ) { + val candidates = candidatesWithoutSmartcast + ?: candidatesWithSmartcast?.values?.map { it.memberWithBaseScope } + ?: error("candidatesWithoutSmartcast or candidatesWithSmartcast should be not null") + for (candidateWithScope in candidates) { val (candidate, scope) = candidateWithScope if (candidate.hasConsistentExtensionReceiver(givenExtensionReceiverOptions)) { - val isFromOriginalTypeInPresenceOfSmartCast = isFromSmartCast != null && !isFromSmartCast.getValue(candidateWithScope) + val isFromOriginalTypeInPresenceOfSmartCast = candidatesWithSmartcast != null && + !candidatesWithSmartcast.getValue(candidateWithScope.member).cameFromSmartcast val dispatchReceiverToUse = when { isFromOriginalTypeInPresenceOfSmartCast -> diff --git a/compiler/fir/tree/src/org/jetbrains/kotlin/fir/scopes/FirTypeScope.kt b/compiler/fir/tree/src/org/jetbrains/kotlin/fir/scopes/FirTypeScope.kt index 956c61000e6..db458d063dc 100644 --- a/compiler/fir/tree/src/org/jetbrains/kotlin/fir/scopes/FirTypeScope.kt +++ b/compiler/fir/tree/src/org/jetbrains/kotlin/fir/scopes/FirTypeScope.kt @@ -64,18 +64,7 @@ abstract class FirTypeScope : FirContainingNamesAwareScope() { } } -class MemberWithBaseScope>(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() - } -} +data class MemberWithBaseScope>(val member: D, val baseScope: FirTypeScope) typealias ProcessOverriddenWithBaseScope = FirTypeScope.(D, (D, FirTypeScope) -> ProcessorAction) -> ProcessorAction typealias ProcessAllOverridden = FirTypeScope.(D, (D) -> ProcessorAction) -> ProcessorAction