From 1bacef43271709564b42ce3c9502096ff2606733 Mon Sep 17 00:00:00 2001 From: "Denis.Zharkov" Date: Thu, 26 Jan 2023 15:25:36 +0100 Subject: [PATCH] K2: Simplify handling mixed smartcast vs. original candidates There's no real need in binding overrides for smartcast/original members after FilteringOutOriginalInPresenceOfSmartCastConeCallConflictResolver Instead, we just create candidates for all of them and filter out after the ones that came from original type (if needed) --- .../fir/resolve/calls/tower/TowerLevels.kt | 63 ++++++------------- 1 file changed, 19 insertions(+), 44 deletions(-) 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 3f7d642d682..47e1e8d5822 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 @@ -78,7 +78,6 @@ class MemberScopeTowerLevel( private val session: FirSession get() = bodyResolveComponents.session private fun > processMembers( - callInfo: CallInfo, output: TowerScopeLevelProcessor, processScopeMembers: FirScope.(processor: (T) -> Unit) -> Unit ): ProcessResult { @@ -96,16 +95,25 @@ class MemberScopeTowerLevel( if (scopeWithoutSmartcast == null) { consumeCandidates(output, candidates) } else { - val candidatesFromOriginalType = mutableListOf>() + val isFromSmartCast: MutableMap, Boolean> = mutableMapOf() + scopeWithoutSmartcast.collectCandidates(processScopeMembers).let { (isEmpty, originalCandidates) -> empty = empty && isEmpty - candidatesFromOriginalType += originalCandidates + for (originalCandidate in originalCandidates) { + isFromSmartCast[originalCandidate] = false + } } - if (candidatesFromOriginalType.isNotEmpty()) { - processMembersFromSmartcastedType(callInfo, candidatesFromOriginalType, candidates, output) - } else { - consumeCandidates(output, candidates) + + for (candidateFromSmartCast in candidates) { + isFromSmartCast[candidateFromSmartCast] = true } + + consumeCandidates( + output, + // all the candidates, both from original type and smart cast + candidates = isFromSmartCast.keys, + isFromSmartCast, + ) } if (givenExtensionReceiverOptions.isEmpty()) { @@ -153,39 +161,6 @@ class MemberScopeTowerLevel( return if (empty) ProcessResult.SCOPE_EMPTY else ProcessResult.FOUND } - private fun > processMembersFromSmartcastedType( - callInfo: CallInfo, - candidatesFromOriginalType: Collection>, - candidatesFromSmartcast: Collection>, - output: TowerScopeLevelProcessor, - ) { - val visibilityChecker = session.visibilityChecker - val candidatesMapping = buildMap { - candidatesFromOriginalType.forEach { put(it, false) } - candidatesFromSmartcast.forEach { put(it, true) } - } - - val candidates = mutableListOf>() - - // The code below is assumed to be for sake of optimization only. - // It helps to avoid creating candidates both for some member in the original type and for its override in the smart cast - // when they are both visible. - // But semantically it should be OK to declare `val candidates` as `candidatesMapping.keys.toList()` - val overridableGroups = session.overrideService.createOverridableGroups( - candidatesFromOriginalType + candidatesFromSmartcast, - FirIntersectionScopeOverrideChecker(session) - ) - for (group in overridableGroups) { - val visibleCandidates = group.filter { - visibilityChecker.isVisible(it.member.fir, callInfo, dispatchReceiverValue) - } - - val visibleCandidatesFromSmartcast = visibleCandidates.filter { candidatesMapping.getValue(it) } - candidates += visibleCandidatesFromSmartcast.ifEmpty { group } - } - consumeCandidates(output, candidates, candidatesMapping) - } - private fun > FirTypeScope.collectCandidates( processScopeMembers: FirScope.(processor: (T) -> Unit) -> Unit ): Pair>> { @@ -206,13 +181,13 @@ class MemberScopeTowerLevel( private fun > consumeCandidates( output: TowerScopeLevelProcessor, - candidatesWithScope: List>, + candidates: 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 ) { - for (candidateWithScope in candidatesWithScope) { + for (candidateWithScope in candidates) { val (candidate, scope) = candidateWithScope if (candidate.hasConsistentExtensionReceiver(givenExtensionReceiverOptions)) { val isFromOriginalTypeInPresenceOfSmartCast = isFromSmartCast != null && !isFromSmartCast.getValue(candidateWithScope) @@ -244,7 +219,7 @@ class MemberScopeTowerLevel( processor: TowerScopeLevelProcessor> ): ProcessResult { val lookupTracker = session.lookupTracker - return processMembers(info, processor) { consumer -> + return processMembers(processor) { consumer -> withMemberCallLookup(lookupTracker, info) { lookupCtx -> this.processFunctionsAndConstructorsByName( info, session, bodyResolveComponents, @@ -264,7 +239,7 @@ class MemberScopeTowerLevel( processor: TowerScopeLevelProcessor> ): ProcessResult { val lookupTracker = session.lookupTracker - return processMembers(info, processor) { consumer -> + return processMembers(processor) { consumer -> withMemberCallLookup(lookupTracker, info) { lookupCtx -> lookupTracker?.recordCallLookup(info, dispatchReceiverValue.type) this.processPropertiesByName(info.name) {