Partially implement invoke resolution
diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/CallResolver.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/CallResolver.kt
index 2af5b46..6309e4f 100644
--- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/CallResolver.kt
+++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/CallResolver.kt
@@ -96,7 +96,8 @@
     val implicitExtensionReceiverValue: ImplicitReceiverValue?,
     val explicitReceiverKind: ExplicitReceiverKind,
     private val inferenceComponents: InferenceComponents,
-    private val baseSystem: ConstraintStorage
+    private val baseSystem: ConstraintStorage,
+    val callInfo: CallInfo
 ) {
     val system by lazy {
         val system = inferenceComponents.createConstraintSystem()
@@ -280,7 +281,8 @@
                 if (candidate.hasConsistentExtensionReceiver(extensionReceiver) && candidate.dispatchReceiverValue() == null) {
                     processor.consumeCandidate(
                         candidate as T, dispatchReceiverValue = null,
-                        implicitExtensionReceiverValue = implicitExtensionReceiver)
+                        implicitExtensionReceiverValue = implicitExtensionReceiver
+                    )
                 } else {
                     ProcessorAction.NEXT
                 }
@@ -349,7 +351,7 @@
         towerScopeLevel: TowerScopeLevel,
         group: Int
     ): ProcessorAction {
-        if (checkSkip(group, resultCollector)) return ProcessorAction.NEXT
+        if (skipGroup(group, resultCollector)) return ProcessorAction.NEXT
         if (kind != TowerDataKind.EMPTY) return ProcessorAction.NEXT
 
         return QualifiedReceiverTowerLevel(session).processElementsByName(
@@ -388,12 +390,20 @@
         group: Int
     ): ProcessorAction
 
+    private var processed = -1
     private var stopGroup = Int.MAX_VALUE
-    fun checkSkip(group: Int, resultCollector: CandidateCollector): Boolean {
+    fun skipGroup(group: Int, resultCollector: CandidateCollector): Boolean {
         if (resultCollector.isSuccess() && stopGroup == Int.MAX_VALUE) {
             stopGroup = group
         }
-        return group > stopGroup
+        if (group > processed) {
+            processed = group
+        }
+        if (group < processed) {
+            return true
+        }
+        if (group > stopGroup) return true
+        return false
     }
 }
 
@@ -452,6 +462,17 @@
     resultCollector: CandidateCollector,
     callResolver: CallResolver
 ): TowerDataConsumer {
+    val varCallInfo = CallInfo(
+        CallKind.VariableAccess,
+        callInfo.explicitReceiver,
+        emptyList(),
+        callInfo.isSafeCall,
+        callInfo.typeArguments,
+        inferenceComponents.session,
+        callInfo.containingFile,
+        callInfo.container,
+        callInfo.typeProvider
+    )
     return PrioritizedTowerDataConsumer(
         resultCollector,
         createSimpleConsumer(
@@ -462,29 +483,23 @@
             inferenceComponents,
             resultCollector
         ),
-        createSimpleConsumer(
-            session,
-            name,
-            TowerScopeLevel.Token.Properties,
-            callInfo,
-            inferenceComponents,
-            InvokeCandidateCollector(
-                callResolver,
-                varCallInfo = CallInfo(
-                    CallKind.VariableAccess,
-                    callInfo.explicitReceiver,
-                    emptyList(),
-                    callInfo.isSafeCall,
-                    callInfo.typeArguments,
-                    inferenceComponents.session,
-                    callInfo.containingFile,
-                    callInfo.container,
-                    callInfo.typeProvider
-                ),
-                invokeCallInfo = callInfo,
-                components = inferenceComponents
+        MultiplexerTowerDataConsumer(resultCollector).apply {
+            addConsumer(
+                createSimpleConsumer(
+                    session,
+                    name,
+                    TowerScopeLevel.Token.Properties,
+                    varCallInfo,
+                    inferenceComponents,
+                    InvokeCandidateCollector(
+                        callResolver,
+                        invokeCallInfo = callInfo,
+                        components = inferenceComponents,
+                        multiplexer = this
+                    )
+                )
             )
-        )
+        }
     )
 }
 
@@ -534,7 +549,7 @@
         towerScopeLevel: TowerScopeLevel,
         group: Int
     ): ProcessorAction {
-        if (checkSkip(group, resultCollector)) return ProcessorAction.NEXT
+        if (skipGroup(group, resultCollector)) return ProcessorAction.NEXT
         for ((index, consumer) in consumers.withIndex()) {
             val action = consumer.consume(kind, towerScopeLevel, group * consumers.size + index)
             if (action.stop()) {
@@ -545,6 +560,52 @@
     }
 }
 
+class MultiplexerTowerDataConsumer(
+    val resultCollector: CandidateCollector
+) : TowerDataConsumer() {
+
+    val consumers = mutableListOf<TowerDataConsumer>()
+    val newConsumers = mutableListOf<TowerDataConsumer>()
+
+    val kinds = mutableListOf<TowerDataKind>()
+    val groups = mutableListOf<Int>()
+    val levels = mutableListOf<TowerScopeLevel>()
+
+    override fun consume(
+        kind: TowerDataKind,
+        towerScopeLevel: TowerScopeLevel,
+        group: Int
+    ): ProcessorAction {
+        if (skipGroup(group, resultCollector)) return ProcessorAction.NEXT
+        consumers += newConsumers
+        newConsumers.clear()
+        kinds += kind
+        groups += group
+        levels += towerScopeLevel
+
+        for (consumer in consumers) {
+            val action = consumer.consume(kind, towerScopeLevel, group)
+            if (action.stop()) {
+                return ProcessorAction.STOP
+            }
+        }
+        return ProcessorAction.NEXT
+    }
+
+    fun addConsumer(consumer: TowerDataConsumer): ProcessorAction =
+        run {
+            for (index in kinds.indices) {
+                if (consumer.consume(kinds[index], levels[index], groups[index]).stop()) {
+                    return@run ProcessorAction.STOP
+                }
+            }
+            return@run ProcessorAction.NEXT
+        }.also {
+            newConsumers += consumer
+        }
+}
+
+
 class ExplicitReceiverTowerDataConsumer<T : ConeSymbol>(
     val session: FirSession,
     val name: Name,
@@ -560,7 +621,7 @@
         towerScopeLevel: TowerScopeLevel,
         group: Int
     ): ProcessorAction {
-        if (checkSkip(group, resultCollector)) return ProcessorAction.NEXT
+        if (skipGroup(group, resultCollector)) return ProcessorAction.NEXT
         return when (kind) {
             TowerDataKind.EMPTY ->
                 MemberScopeTowerLevel(session, explicitReceiver).processElementsByName(
@@ -631,7 +692,7 @@
         towerScopeLevel: TowerScopeLevel,
         group: Int
     ): ProcessorAction {
-        if (checkSkip(group, resultCollector)) return ProcessorAction.NEXT
+        if (skipGroup(group, resultCollector)) return ProcessorAction.NEXT
         return when (kind) {
 
             TowerDataKind.TOWER_LEVEL -> {
@@ -703,20 +764,14 @@
         return group
     }
 
-    val collector = CandidateCollector(callInfo!!, components)
-    private lateinit var towerDataConsumer: TowerDataConsumer
+    val collector by lazy { CandidateCollector(components) }
+    lateinit var towerDataConsumer: TowerDataConsumer
     private lateinit var implicitReceiverValues: List<ImplicitReceiverValue>
 
-    fun runTowerResolver(towerDataConsumer: TowerDataConsumer, implicitReceiverValues: List<ImplicitReceiverValue>): CandidateCollector {
-        this.towerDataConsumer = towerDataConsumer
+    fun runTowerResolver(consumer: TowerDataConsumer, implicitReceiverValues: List<ImplicitReceiverValue>): CandidateCollector {
         this.implicitReceiverValues = implicitReceiverValues
+        towerDataConsumer = consumer
 
-        runTowerResolver()
-
-        return collector
-    }
-
-    fun runTowerResolver(towerDataConsumer: TowerDataConsumer = this.towerDataConsumer) {
         var group = 0
 
         towerDataConsumer.consume(TowerDataKind.EMPTY, TowerScopeLevel.Empty, group++)
@@ -739,8 +794,9 @@
         }
 
 
-    }
 
+        return collector
+    }
 
 }
 
@@ -755,9 +811,7 @@
 }
 
 
-var ID = ""
-
-open class CandidateCollector(val callInfo: CallInfo, val components: InferenceComponents) {
+open class CandidateCollector(val components: InferenceComponents) {
 
     val groupNumbers = mutableListOf<Int>()
     val candidates = mutableListOf<Candidate>()
@@ -780,8 +834,8 @@
         val sink = CheckerSinkImpl(components)
         var finished = false
         sink.continuation = suspend {
-            for (stage in callInfo.callKind.sequence()) {
-                stage.check(candidate, sink, callInfo)
+            for (stage in candidate.callInfo.callKind.sequence()) {
+                stage.check(candidate, sink, candidate.callInfo)
             }
         }.createCoroutineUnintercepted(completion = object : Continuation<Unit> {
             override val context: CoroutineContext
@@ -847,8 +901,11 @@
 }
 
 class InvokeCandidateCollector(
-    val callResolver: CallResolver, val varCallInfo: CallInfo, val invokeCallInfo: CallInfo, components: InferenceComponents
-) : CandidateCollector(varCallInfo, components) {
+    val callResolver: CallResolver,
+    val invokeCallInfo: CallInfo,
+    components: InferenceComponents,
+    val multiplexer: MultiplexerTowerDataConsumer
+) : CandidateCollector(components) {
     override fun consumeCandidate(group: Int, candidate: Candidate): CandidateApplicability {
         val applicability = super.consumeCandidate(group, candidate)
 
@@ -858,7 +915,13 @@
             val boundInvokeCallInfo = CallInfo(
                 invokeCallInfo.callKind,
                 FirQualifiedAccessExpressionImpl(session, null, false).apply {
-                    calleeReference = FirNamedReferenceWithCandidate(session, null, (candidate.symbol as ConeCallableSymbol).callableId.callableName, candidate)
+                    calleeReference = FirNamedReferenceWithCandidate(
+                        session,
+                        null,
+                        (candidate.symbol as ConeCallableSymbol).callableId.callableName,
+                        candidate
+                    )
+                    typeRef = callResolver.typeCalculator.tryCalculateReturnType(candidate.symbol.firUnsafe())
                 },
                 invokeCallInfo.arguments,
                 invokeCallInfo.isSafeCall,
@@ -868,9 +931,10 @@
                 invokeCallInfo.container,
                 invokeCallInfo.typeProvider
             )
-            val invokeConsumer = createSimpleFunctionConsumer(session, Name.identifier("invoke"), boundInvokeCallInfo, components, callResolver.collector)
+            val invokeConsumer =
+                createSimpleFunctionConsumer(session, Name.identifier("invoke"), boundInvokeCallInfo, components, callResolver.collector)
 
-            callResolver.runTowerResolver(invokeConsumer)
+            multiplexer.addConsumer(invokeConsumer)
         }
 
         return applicability
diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/CandidateFactory.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/CandidateFactory.kt
index 4929f89b0..fc76643 100644
--- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/CandidateFactory.kt
+++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/CandidateFactory.kt
@@ -15,7 +15,7 @@
 
 class CandidateFactory(
     val inferenceComponents: InferenceComponents,
-    callInfo: CallInfo
+    val callInfo: CallInfo
 ) {
 
     val baseSystem: ConstraintStorage
@@ -37,7 +37,7 @@
     ): Candidate {
         return Candidate(
             symbol, dispatchReceiverValue, implicitExtensionReceiverValue,
-            explicitReceiverKind, inferenceComponents, baseSystem
+            explicitReceiverKind, inferenceComponents, baseSystem, callInfo
         )
     }
 }
diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/FirBodyResolveTransformer.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/FirBodyResolveTransformer.kt
index c584450..e744342 100644
--- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/FirBodyResolveTransformer.kt
+++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/FirBodyResolveTransformer.kt
@@ -348,7 +348,8 @@
         val consumer = createVariableAndObjectConsumer(
             session,
             callee.name,
-            info, inferenceComponents
+            info, inferenceComponents,
+            resolver.collector
         )
         val result = resolver.runTowerResolver(consumer, implicitReceiverStack.asReversed())
 
@@ -551,7 +552,7 @@
         resolver.callInfo = info
         resolver.scopes = (scopes + localScopes).asReversed()
 
-        val consumer = createFunctionConsumer(session, name, info, inferenceComponents)
+        val consumer = createFunctionConsumer(session, name, info, inferenceComponents, resolver.collector, resolver)
         val result = resolver.runTowerResolver(consumer, implicitReceiverStack.asReversed())
         val bestCandidates = result.bestCandidates()
         val reducedCandidates = if (result.currentApplicability < CandidateApplicability.SYNTHETIC_RESOLVED) {
diff --git a/compiler/fir/resolve/testData/resolve/expresssions/invoke/explicitReceiver.txt b/compiler/fir/resolve/testData/resolve/expresssions/invoke/explicitReceiver.txt
index 945af23..4ee5801 100644
--- a/compiler/fir/resolve/testData/resolve/expresssions/invoke/explicitReceiver.txt
+++ b/compiler/fir/resolve/testData/resolve/expresssions/invoke/explicitReceiver.txt
@@ -13,8 +13,8 @@
             ^invoke this#
         }
 
-        public final fun bar(): R|kotlin/Unit| {
-            ^bar R|/x|()
+        public final fun bar(): R|Foo| {
+            ^bar R|/Foo.invoke|()
         }
 
     }
diff --git a/compiler/fir/resolve/testData/resolve/expresssions/invoke/explicitReceiver2.txt b/compiler/fir/resolve/testData/resolve/expresssions/invoke/explicitReceiver2.txt
index 796a513..78d6d9d 100644
--- a/compiler/fir/resolve/testData/resolve/expresssions/invoke/explicitReceiver2.txt
+++ b/compiler/fir/resolve/testData/resolve/expresssions/invoke/explicitReceiver2.txt
@@ -23,8 +23,8 @@
         public final val x: R|Bar| = R|/Bar.Bar|()
             public get(): R|Bar|
 
-        public final fun bar(): R|kotlin/Unit| {
-            ^bar R|/x|()
+        public final fun bar(): R|Foo| {
+            ^bar R|/Bar.invoke|()
         }
 
     }
diff --git a/compiler/fir/resolve/testData/resolve/expresssions/invoke/extension.kt b/compiler/fir/resolve/testData/resolve/expresssions/invoke/extension.kt
index 2c22274..ebe97fb 100644
--- a/compiler/fir/resolve/testData/resolve/expresssions/invoke/extension.kt
+++ b/compiler/fir/resolve/testData/resolve/expresssions/invoke/extension.kt
@@ -7,5 +7,5 @@
 
     val x = 0
 
-    fun foo() = x()
+    fun foo() = x() // should resolve to invoke
 }
\ No newline at end of file
diff --git a/compiler/fir/resolve/testData/resolve/expresssions/invoke/farInvokeExtension.kt b/compiler/fir/resolve/testData/resolve/expresssions/invoke/farInvokeExtension.kt
index eaf10f0..3b46415 100644
--- a/compiler/fir/resolve/testData/resolve/expresssions/invoke/farInvokeExtension.kt
+++ b/compiler/fir/resolve/testData/resolve/expresssions/invoke/farInvokeExtension.kt
@@ -8,5 +8,5 @@
 
     val x = 0
 
-    fun foo() = x()
+    fun foo() = x() // should resolve to fun x
 }
\ No newline at end of file
diff --git a/compiler/fir/resolve/testData/resolve/expresssions/invoke/implicitTypeOrder.kt b/compiler/fir/resolve/testData/resolve/expresssions/invoke/implicitTypeOrder.kt
index 47deb73..81e849c 100644
--- a/compiler/fir/resolve/testData/resolve/expresssions/invoke/implicitTypeOrder.kt
+++ b/compiler/fir/resolve/testData/resolve/expresssions/invoke/implicitTypeOrder.kt
@@ -1,6 +1,6 @@
 
 class A {
-    fun bar() = foo()
+    fun bar() = foo() // should resolve to invoke
 
     fun invoke() = this
 }
diff --git a/compiler/fir/resolve/testData/resolve/expresssions/invoke/implicitTypeOrder.txt b/compiler/fir/resolve/testData/resolve/expresssions/invoke/implicitTypeOrder.txt
index e470dc3..85af0fc 100644
--- a/compiler/fir/resolve/testData/resolve/expresssions/invoke/implicitTypeOrder.txt
+++ b/compiler/fir/resolve/testData/resolve/expresssions/invoke/implicitTypeOrder.txt
@@ -4,8 +4,8 @@
             super<R|kotlin/Any|>()
         }
 
-        public final fun bar(): <ERROR TYPE REF: Unresolved name: foo> {
-            ^bar <Unresolved name: foo>#()
+        public final fun bar(): R|A| {
+            ^bar R|/A.invoke|()
         }
 
         public final fun invoke(): R|A| {
diff --git a/compiler/fir/resolve/testData/resolve/expresssions/invoke/simple.txt b/compiler/fir/resolve/testData/resolve/expresssions/invoke/simple.txt
index c12629e..06667b5 100644
--- a/compiler/fir/resolve/testData/resolve/expresssions/invoke/simple.txt
+++ b/compiler/fir/resolve/testData/resolve/expresssions/invoke/simple.txt
@@ -10,5 +10,5 @@
 
     }
     public final fun test(s: R|Simple|): R|kotlin/Unit| {
-        lval result: <ERROR TYPE REF: Unresolved name: s> = <Unresolved name: s>#()
+        lval result: R|kotlin/String| = R|/Simple.invoke|()
     }