diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/FirCallResolver.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/FirCallResolver.kt index 135331669de..60e89266425 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/FirCallResolver.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/FirCallResolver.kt @@ -39,6 +39,7 @@ import org.jetbrains.kotlin.fir.types.impl.FirResolvedTypeRefImpl import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.resolve.calls.inference.ConstraintSystemBuilder import org.jetbrains.kotlin.resolve.calls.results.TypeSpecificityComparator +import org.jetbrains.kotlin.resolve.descriptorUtil.HIDES_MEMBERS_NAME_LIST class FirCallResolver( components: BodyResolveComponents, @@ -92,7 +93,11 @@ class FirCallResolver( towerResolver.reset() val consumer = createFunctionConsumer(session, name, info, this, towerResolver.collector, towerResolver) - val result = towerResolver.runResolver(consumer, implicitReceiverStack.receiversAsReversed()) + val result = towerResolver.runResolver( + consumer, + implicitReceiverStack.receiversAsReversed(), + shouldProcessExtensionsBeforeMembers = name in HIDES_MEMBERS_NAME_LIST + ) val bestCandidates = result.bestCandidates() val reducedCandidates = if (result.currentApplicability < CandidateApplicability.SYNTHETIC_RESOLVED) { bestCandidates.toSet() diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/FirTowerResolver.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/FirTowerResolver.kt index 4be040cb843..30f6c041790 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/FirTowerResolver.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/FirTowerResolver.kt @@ -140,6 +140,39 @@ class FirTowerResolver( return group } + private fun processTopLevelScope( + towerDataConsumer: TowerDataConsumer, + topLevelScope: FirScope, + oldGroup: Int, + extensionsOnly: Boolean = false + ): Int { + var group = oldGroup + // Top-level extensions via implicit receiver + // fun Foo.bar() {} + // class Foo { + // fun test() { bar() } + // } + for (implicitReceiverValue in implicitReceiverValues) { + if (towerDataConsumer.consume( + TOWER_LEVEL, + ScopeTowerLevel( + session, components, topLevelScope, + implicitExtensionReceiver = implicitReceiverValue, + extensionsOnly = extensionsOnly + ), + group++ + ) == NONE + ) { + return group + } + } + // Member of top-level scope & importing scope + // val x = 0 + // fun test() { x } + towerDataConsumer.consume(TOWER_LEVEL, ScopeTowerLevel(session, components, topLevelScope), group++) + return group + } + fun reset() { collector.newDataSet() } @@ -148,11 +181,23 @@ class FirTowerResolver( private lateinit var towerDataConsumer: TowerDataConsumer private lateinit var implicitReceiverValues: List> - fun runResolver(consumer: TowerDataConsumer, implicitReceiverValues: List>): CandidateCollector { + fun runResolver( + consumer: TowerDataConsumer, + implicitReceiverValues: List>, + shouldProcessExtensionsBeforeMembers: Boolean = false + ): CandidateCollector { this.implicitReceiverValues = implicitReceiverValues towerDataConsumer = consumer var group = 0 + + // Specific case when extension should be processed before members (Kotlin forEach vs Java forEach) + if (shouldProcessExtensionsBeforeMembers) { + for (topLevelScope in topLevelScopes) { + group = processTopLevelScope(towerDataConsumer, topLevelScope, group, extensionsOnly = true) + } + } + // Member of explicit receiver' type (this stage does nothing without explicit receiver) // class Foo(val x: Int) // fun test(f: Foo) { f.x } @@ -200,26 +245,8 @@ class FirTowerResolver( } } - topLevelScopeLoop@ for (scope in topLevelScopes) { - // Top-level extensions via implicit receiver - // fun Foo.bar() {} - // class Foo { - // fun test() { bar() } - // } - for (implicitReceiverValue in implicitReceiverValues) { - if (towerDataConsumer.consume( - TOWER_LEVEL, - ScopeTowerLevel(session, components, scope, implicitExtensionReceiver = implicitReceiverValue), - group++ - ) == NONE - ) { - continue@topLevelScopeLoop - } - } - // Member of top-level scope & importing scope - // val x = 0 - // fun test() { x } - towerDataConsumer.consume(TOWER_LEVEL, ScopeTowerLevel(session, components, scope), group++) + for (topLevelScope in topLevelScopes) { + group = processTopLevelScope(towerDataConsumer, topLevelScope, group) } return collector diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/TowerLevels.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/TowerLevels.kt index ba095eef50a..996d118be34 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/TowerLevels.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/TowerLevels.kt @@ -150,12 +150,14 @@ class MemberScopeTowerLevel( // (if explicit receiver exists, it always *should* be an extension receiver) class ScopeTowerLevel( session: FirSession, - val bodyResolveComponents: BodyResolveComponents, + private val bodyResolveComponents: BodyResolveComponents, val scope: FirScope, - val implicitExtensionReceiver: ImplicitReceiverValue<*>? = null + val implicitExtensionReceiver: ImplicitReceiverValue<*>? = null, + private val extensionsOnly: Boolean = false ) : SessionBasedTowerLevel(session) { private fun FirCallableSymbol<*>.hasConsistentReceivers(extensionReceiver: ReceiverValue?): Boolean = when { + extensionsOnly && !hasExtensionReceiver() -> false !hasConsistentExtensionReceiver(extensionReceiver) -> false scope is FirAbstractImportingScope -> true else -> dispatchReceiverValue().let { it == null || it.klassSymbol.fir.classKind == ClassKind.OBJECT } diff --git a/compiler/fir/resolve/testData/resolve/stdlib/j+k/SyntheticWithForEach.kt b/compiler/fir/resolve/testData/resolve/stdlib/j+k/SyntheticWithForEach.kt index 7c309e823b4..447b7f9c975 100644 --- a/compiler/fir/resolve/testData/resolve/stdlib/j+k/SyntheticWithForEach.kt +++ b/compiler/fir/resolve/testData/resolve/stdlib/j+k/SyntheticWithForEach.kt @@ -17,7 +17,7 @@ fun Call.testForEach() { value.length } arguments.forEach { - it.key.length - it.value.length + it.key.length + it.value.length } } \ No newline at end of file diff --git a/compiler/fir/resolve/testData/resolve/stdlib/j+k/SyntheticWithForEach.txt b/compiler/fir/resolve/testData/resolve/stdlib/j+k/SyntheticWithForEach.txt index 5100d37c6e3..f087124cb7d 100644 --- a/compiler/fir/resolve/testData/resolve/stdlib/j+k/SyntheticWithForEach.txt +++ b/compiler/fir/resolve/testData/resolve/stdlib/j+k/SyntheticWithForEach.txt @@ -5,9 +5,9 @@ FILE: test.kt R|/value|.R|kotlin/String.length| } ) - R|/Call.arguments|.R|FakeOverride|( = forEach@fun (): R|kotlin/Unit| { - #.#.# - #.#.# + R|/Call.arguments|.R|kotlin/collections/forEach|!|, R|ft!|>( = forEach@fun (it: R|kotlin/collections/Map.Entry!, ft!>|): R|kotlin/Unit| { + R|/it|.R|FakeOverride!|>|.R|kotlin/String.length| + R|/it|.R|FakeOverride!|>|.R|kotlin/String.length| } ) } diff --git a/compiler/fir/resolve/testData/resolve/stdlib/noneWithForEach.kt b/compiler/fir/resolve/testData/resolve/stdlib/noneWithForEach.kt index 01dc87cbff6..da161893516 100644 --- a/compiler/fir/resolve/testData/resolve/stdlib/noneWithForEach.kt +++ b/compiler/fir/resolve/testData/resolve/stdlib/noneWithForEach.kt @@ -7,10 +7,10 @@ fun foo(conflicting: List) { conflicting.groupBy { it.name }.forEach { - val diagnostics = it.value + val diagnostics = it.value filtered.addAll( - diagnostics.filter { me -> - diagnostics.none { other -> + diagnostics.filter { me -> + diagnostics.none { other -> me != other } } diff --git a/compiler/fir/resolve/testData/resolve/stdlib/noneWithForEach.txt b/compiler/fir/resolve/testData/resolve/stdlib/noneWithForEach.txt index 37687eb78a2..06658ff6d0f 100644 --- a/compiler/fir/resolve/testData/resolve/stdlib/noneWithForEach.txt +++ b/compiler/fir/resolve/testData/resolve/stdlib/noneWithForEach.txt @@ -9,10 +9,10 @@ FILE: noneWithForEach.kt R|/conflicting|.R|kotlin/collections/groupBy|( = groupBy@fun (it: R|Diagnostic|): R|kotlin/String| { R|/it|.R|/Diagnostic.name| } - ).R|FakeOverride|( = forEach@fun (): R|kotlin/Unit| { - lval diagnostics: = #.# - R|/filtered|.R|FakeOverride|(R|/diagnostics|.#( = filter@fun (me: R|class error: No type for parameter|): { - R|/diagnostics|.#( = none@fun (other: R|class error: No type for parameter|): R|kotlin/Boolean| { + ).R|kotlin/collections/forEach||>( = forEach@fun (it: R|kotlin/collections/Map.Entry>|): R|kotlin/Unit| { + lval diagnostics: R|kotlin/collections/List| = R|/it|.R|FakeOverride|>| + R|/filtered|.R|FakeOverride|(R|/diagnostics|.R|kotlin/collections/filter|( = filter@fun (me: R|Diagnostic|): R|kotlin/Boolean| { + R|/diagnostics|.R|kotlin/collections/none|( = none@fun (other: R|Diagnostic|): R|kotlin/Boolean| { !=(R|/me|, R|/other|) } )