~ WIP
diff --git a/.idea/dictionaries/Denis_Zharkov.xml b/.idea/dictionaries/Denis_Zharkov.xml
new file mode 100644
index 0000000..3251fd1
--- /dev/null
+++ b/.idea/dictionaries/Denis_Zharkov.xml
@@ -0,0 +1,7 @@
+<component name="ProjectDictionaryState">
+  <dictionary name="Denis.Zharkov">
+    <words>
+      <w>pcla</w>
+    </words>
+  </dictionary>
+</component>
\ No newline at end of file
diff --git a/compiler/fir/build.gradle.kts b/compiler/fir/build.gradle.kts
index aff8f2e..af70e2c 100644
--- a/compiler/fir/build.gradle.kts
+++ b/compiler/fir/build.gradle.kts
@@ -22,6 +22,7 @@
         tasks.withType<org.jetbrains.kotlin.gradle.dsl.KotlinCompile<*>>().configureEach {
             kotlinOptions {
                 freeCompilerArgs += "-opt-in=org.jetbrains.kotlin.fir.symbols.SymbolInternals"
+                freeCompilerArgs += "-opt-in=org.jetbrains.kotlin.types.model.K2Only"
             }
         }
     }
diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/diagnostics/coneDiagnosticToFirDiagnostic.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/diagnostics/coneDiagnosticToFirDiagnostic.kt
index 69fa4dd..9636df5 100644
--- a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/diagnostics/coneDiagnosticToFirDiagnostic.kt
+++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/diagnostics/coneDiagnosticToFirDiagnostic.kt
@@ -16,7 +16,6 @@
 import org.jetbrains.kotlin.fir.analysis.checkers.declaration.isLocalMember
 import org.jetbrains.kotlin.fir.analysis.getChild
 import org.jetbrains.kotlin.fir.builder.FirSyntaxErrors
-import org.jetbrains.kotlin.fir.declarations.FirAnonymousFunction
 import org.jetbrains.kotlin.fir.declarations.FirMemberDeclaration
 import org.jetbrains.kotlin.fir.declarations.utils.*
 import org.jetbrains.kotlin.fir.diagnostics.*
@@ -317,13 +316,13 @@
                     rootCause.expectedContextReceiverType.removeTypeVariableTypes(typeContext)
                 )
 
-            is StubBuilderInferenceReceiver -> {
-                val typeParameterSymbol = rootCause.typeParameterSymbol
+            is TypeVariableAsExplicitReceiver -> {
+                val typeParameter = rootCause.typeParameter
                 @OptIn(SymbolInternals::class)
                 FirErrors.BUILDER_INFERENCE_STUB_RECEIVER.createOn(
-                    qualifiedAccessSource ?: source,
-                    typeParameterSymbol.name,
-                    (typeParameterSymbol.containingDeclarationSymbol.fir as FirMemberDeclaration).nameOrSpecialName
+                    rootCause.explicitReceiver.source,
+                    typeParameter.symbol.name,
+                    (typeParameter.symbol.containingDeclarationSymbol.fir as FirMemberDeclaration).nameOrSpecialName
                 )
             }
 
diff --git a/compiler/fir/cones/src/org/jetbrains/kotlin/fir/types/ConeTypeVariableAndStubTypes.kt b/compiler/fir/cones/src/org/jetbrains/kotlin/fir/types/ConeTypeVariableAndStubTypes.kt
index 3444ca8..85eae6a 100644
--- a/compiler/fir/cones/src/org/jetbrains/kotlin/fir/types/ConeTypeVariableAndStubTypes.kt
+++ b/compiler/fir/cones/src/org/jetbrains/kotlin/fir/types/ConeTypeVariableAndStubTypes.kt
@@ -46,6 +46,8 @@
     fun recordInfoAboutTypeVariableUsagesAsInvariantOrContravariantParameter() {
         isContainedInInvariantOrContravariantPositions = true
     }
+
+    override fun toString(): String = "${this::class.simpleName}($debugName)"
 }
 
 // ----------------------------------- Stub types -----------------------------------
diff --git a/compiler/fir/providers/src/org/jetbrains/kotlin/fir/resolve/ScopeUtils.kt b/compiler/fir/providers/src/org/jetbrains/kotlin/fir/resolve/ScopeUtils.kt
index 9878b0f..f0d695d 100644
--- a/compiler/fir/providers/src/org/jetbrains/kotlin/fir/resolve/ScopeUtils.kt
+++ b/compiler/fir/providers/src/org/jetbrains/kotlin/fir/resolve/ScopeUtils.kt
@@ -125,6 +125,13 @@
     is ConeDefinitelyNotNullType -> original.scope(useSiteSession, scopeSession, requiredMembersPhase)
     is ConeIntegerConstantOperatorType -> scopeSession.getOrBuildScopeForIntegerConstantOperatorType(useSiteSession, this)
     is ConeIntegerLiteralConstantType -> error("ILT should not be in receiver position")
+    // See testData/diagnostics/tests/inference/builderInference/memberScopeOfCapturedTypeForPostponedCall.kt
+    is ConeCapturedType -> {
+        val supertypes =
+            constructor.supertypes?.takeIf { it.isNotEmpty() }
+                ?: listOf(useSiteSession.builtinTypes.anyType.type)
+        useSiteSession.typeContext.intersectTypes(supertypes).scope(useSiteSession, scopeSession, requiredMembersPhase)
+    }
     else -> null
 }
 
diff --git a/compiler/fir/providers/src/org/jetbrains/kotlin/fir/resolve/calls/FirReceivers.kt b/compiler/fir/providers/src/org/jetbrains/kotlin/fir/resolve/calls/FirReceivers.kt
index d0290e8..9c6b447 100644
--- a/compiler/fir/providers/src/org/jetbrains/kotlin/fir/resolve/calls/FirReceivers.kt
+++ b/compiler/fir/providers/src/org/jetbrains/kotlin/fir/resolve/calls/FirReceivers.kt
@@ -9,7 +9,6 @@
 import org.jetbrains.kotlin.fakeElement
 import org.jetbrains.kotlin.fir.FirSession
 import org.jetbrains.kotlin.fir.declarations.FirResolvePhase
-import org.jetbrains.kotlin.fir.diagnostics.ConeIntermediateDiagnostic
 import org.jetbrains.kotlin.fir.expressions.FirCheckNotNullCall
 import org.jetbrains.kotlin.fir.expressions.FirExpression
 import org.jetbrains.kotlin.fir.expressions.FirSmartCastExpression
@@ -18,7 +17,6 @@
 import org.jetbrains.kotlin.fir.expressions.builder.buildSmartCastExpression
 import org.jetbrains.kotlin.fir.expressions.builder.buildThisReceiverExpression
 import org.jetbrains.kotlin.fir.references.builder.buildImplicitThisReference
-import org.jetbrains.kotlin.fir.renderWithType
 import org.jetbrains.kotlin.fir.resolve.ScopeSession
 import org.jetbrains.kotlin.fir.resolve.scope
 import org.jetbrains.kotlin.fir.resolve.smartcastScope
@@ -28,8 +26,10 @@
 import org.jetbrains.kotlin.fir.symbols.impl.FirCallableSymbol
 import org.jetbrains.kotlin.fir.symbols.impl.FirClassSymbol
 import org.jetbrains.kotlin.fir.symbols.impl.FirScriptSymbol
-import org.jetbrains.kotlin.fir.types.*
+import org.jetbrains.kotlin.fir.types.ConeKotlinType
 import org.jetbrains.kotlin.fir.types.builder.buildResolvedTypeRef
+import org.jetbrains.kotlin.fir.types.constructType
+import org.jetbrains.kotlin.fir.types.resolvedType
 import org.jetbrains.kotlin.name.Name
 import org.jetbrains.kotlin.types.SmartcastStability
 
@@ -89,6 +89,7 @@
 
     abstract val isContextReceiver: Boolean
 
+    // Type before smart cast
     val originalType: ConeKotlinType = type
 
     var implicitScope: FirTypeScope? =
@@ -145,19 +146,6 @@
     @RequiresOptIn
     annotation class ImplicitReceiverInternals
 
-    @Deprecated(level = DeprecationLevel.ERROR, message = "Builder inference should not modify implicit receivers. KT-54708")
-    fun updateTypeInBuilderInference(type: ConeKotlinType) {
-        this.type = type
-        originalReceiverExpression = receiverExpression(boundSymbol, type, contextReceiverNumber, inaccessibleReceiver)
-        _receiverExpression = null
-        implicitScope = type.scope(
-            useSiteSession = useSiteSession,
-            scopeSession = scopeSession,
-            callableCopyTypeCalculator = CallableCopyTypeCalculator.DoNothing,
-            requiredMembersPhase = FirResolvePhase.STATUS,
-        )
-    }
-
     /*
      * Should be called only in ImplicitReceiverStack
      */
diff --git a/compiler/fir/providers/src/org/jetbrains/kotlin/fir/types/ConeInferenceContext.kt b/compiler/fir/providers/src/org/jetbrains/kotlin/fir/types/ConeInferenceContext.kt
index 19cec66..33b4d98 100644
--- a/compiler/fir/providers/src/org/jetbrains/kotlin/fir/types/ConeInferenceContext.kt
+++ b/compiler/fir/providers/src/org/jetbrains/kotlin/fir/types/ConeInferenceContext.kt
@@ -14,10 +14,7 @@
 import org.jetbrains.kotlin.fir.resolve.fullyExpandedType
 import org.jetbrains.kotlin.fir.resolve.providers.FirSymbolProvider
 import org.jetbrains.kotlin.fir.resolve.providers.symbolProvider
-import org.jetbrains.kotlin.fir.resolve.substitution.ConeSubstitutor
-import org.jetbrains.kotlin.fir.resolve.substitution.ConeSubstitutorByMap
-import org.jetbrains.kotlin.fir.resolve.substitution.NoSubstitutor
-import org.jetbrains.kotlin.fir.resolve.substitution.createTypeSubstitutorByTypeConstructor
+import org.jetbrains.kotlin.fir.resolve.substitution.*
 import org.jetbrains.kotlin.fir.symbols.ConeClassLikeLookupTag
 import org.jetbrains.kotlin.fir.symbols.ConeTypeParameterLookupTag
 import org.jetbrains.kotlin.fir.symbols.impl.FirAnonymousObjectSymbol
@@ -205,6 +202,10 @@
             return this.intersectedTypes.any { it.containsInternal(predicate) }
         }
 
+        if (this is ConeCapturedType) {
+            return this.constructor.projection.type?.containsInternal(predicate) == true
+        }
+
         repeat(argumentsCount()) { index ->
             val argument = getArgument(index)
             if (!argument.isStarProjection() && argument.getType().containsInternal(predicate)) return true
@@ -358,6 +359,15 @@
         return ConeSubstitutor.Empty
     }
 
+    override fun createSubstitutionFromSubtypingStubTypesToTypeVariables(): TypeSubstitutorMarker {
+        return object : AbstractConeSubstitutor(this) {
+            override fun substituteType(type: ConeKotlinType): ConeKotlinType? {
+                return ((type as? ConeStubTypeForTypeVariableInSubtyping)
+                    ?.constructor?.variable?.defaultType)?.withNullability(type.nullability, this@ConeInferenceContext)
+            }
+        }
+    }
+
     override fun TypeSubstitutorMarker.safeSubstitute(type: KotlinTypeMarker): KotlinTypeMarker {
         if (this === NoSubstitutor) return type
         require(this is ConeSubstitutor)
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 63aff9e..3fb8066 100644
--- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/FirCallResolver.kt
+++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/FirCallResolver.kt
@@ -26,8 +26,8 @@
 import org.jetbrains.kotlin.fir.resolve.calls.tower.TowerGroup
 import org.jetbrains.kotlin.fir.resolve.calls.tower.TowerResolveManager
 import org.jetbrains.kotlin.fir.resolve.diagnostics.*
-import org.jetbrains.kotlin.fir.resolve.inference.FirBuilderInferenceSession
 import org.jetbrains.kotlin.fir.resolve.inference.ResolvedCallableReferenceAtom
+import org.jetbrains.kotlin.fir.resolve.inference.csBuilder
 import org.jetbrains.kotlin.fir.resolve.inference.inferenceComponents
 import org.jetbrains.kotlin.fir.resolve.substitution.ConeSubstitutor
 import org.jetbrains.kotlin.fir.resolve.transformers.body.resolve.FirAbstractBodyResolveTransformer
@@ -398,10 +398,11 @@
     }
 
     fun resolveCallableReference(
-        constraintSystemBuilder: ConstraintSystemBuilder,
+        containingCallCandidate: Candidate,
         resolvedCallableReferenceAtom: ResolvedCallableReferenceAtom,
         hasSyntheticOuterCall: Boolean,
-    ): Pair<CandidateApplicability, Boolean> {
+    ): Pair<CandidateApplicability, Boolean> = components.context.inferenceSession.runCallableReferenceResolution(containingCallCandidate) {
+        val constraintSystemBuilder = containingCallCandidate.csBuilder
         val callableReferenceAccess = resolvedCallableReferenceAtom.reference
         val calleeReference = callableReferenceAccess.calleeReference
         val lhs = resolvedCallableReferenceAtom.lhs
@@ -450,7 +451,7 @@
                     calleeReference.source
                 )
                 resolvedCallableReferenceAtom.resultingReference = errorReference
-                return applicability to false
+                return@runCallableReferenceResolution applicability to false
             }
             reducedCandidates.size > 1 -> {
                 if (resolvedCallableReferenceAtom.hasBeenPostponed) {
@@ -460,10 +461,10 @@
                         calleeReference.source
                     )
                     resolvedCallableReferenceAtom.resultingReference = errorReference
-                    return applicability to false
+                    return@runCallableReferenceResolution applicability to false
                 }
                 resolvedCallableReferenceAtom.hasBeenPostponed = true
-                return applicability to true
+                return@runCallableReferenceResolution applicability to true
             }
         }
 
@@ -486,7 +487,7 @@
         resolvedCallableReferenceAtom.resultingReference = reference
         resolvedCallableReferenceAtom.resultingTypeForCallableReference = chosenCandidate.resultingTypeForCallableReference
 
-        return applicability to true
+        return@runCallableReferenceResolution applicability to true
     }
 
     fun resolveDelegatingConstructorCall(
@@ -859,7 +860,7 @@
          *   can be important in builder inference mode, and it will never work if we skip completion here.
          * See inferenceFromLambdaReturnStatement.kt test.
          */
-        if (components.context.inferenceSession !is FirBuilderInferenceSession &&
+        if (!candidate.usedOuterCs &&
             createResolvedReferenceWithoutCandidateForLocalVariables &&
             explicitReceiver?.resolvedType !is ConeIntegerLiteralType &&
             coneSymbol is FirVariableSymbol &&
diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/FirOverloadByLambdaReturnTypeResolver.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/FirOverloadByLambdaReturnTypeResolver.kt
index 790bff4..8d7e82f 100644
--- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/FirOverloadByLambdaReturnTypeResolver.kt
+++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/FirOverloadByLambdaReturnTypeResolver.kt
@@ -39,26 +39,6 @@
     ): Set<Candidate> where T : FirStatement, T : FirResolvable {
         if (bestCandidates.size <= 1) return bestCandidates
 
-        /*
-         * Inference session may look into candidate of call, and for that it uses callee reference.
-         * So we need replace reference with proper candidate before calling inference session
-         */
-        val shouldRunCompletion = if (inferenceSession != FirInferenceSession.DEFAULT) {
-            var shouldRunCompletion = true
-            val originalReference = qualifiedAccess.calleeReference
-            for (candidate in bestCandidates) {
-                qualifiedAccess.replaceCalleeReference(FirNamedReferenceWithCandidate(null, candidate.callInfo.name, candidate))
-                shouldRunCompletion = shouldRunCompletion && inferenceSession.shouldRunCompletion(qualifiedAccess)
-                if (!shouldRunCompletion) break
-            }
-            qualifiedAccess.replaceCalleeReference(originalReference)
-            shouldRunCompletion
-        } else {
-            true
-        }
-
-        if (!shouldRunCompletion) return bestCandidates
-
         return reduceCandidatesImpl(
             qualifiedAccess,
             bestCandidates,
@@ -137,7 +117,7 @@
                 firstCandidate.system,
                 firstAtom,
                 firstCandidate,
-                ConstraintSystemCompletionMode.FULL,
+                forOverloadByLambdaReturnType = true,
             )
             while (iterator.hasNext()) {
                 val (candidate, atom) = iterator.next()
@@ -147,7 +127,6 @@
                     atom,
                     candidate,
                     results,
-                    ConstraintSystemCompletionMode.FULL,
                 )
             }
 
diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/FirSpecialTowerDataContexts.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/FirSpecialTowerDataContexts.kt
index 87dbcba..7884c61 100644
--- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/FirSpecialTowerDataContexts.kt
+++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/FirSpecialTowerDataContexts.kt
@@ -7,43 +7,54 @@
 
 import org.jetbrains.kotlin.fir.declarations.FirTowerDataContext
 import org.jetbrains.kotlin.fir.expressions.FirCallableReferenceAccess
+import org.jetbrains.kotlin.fir.resolve.inference.FirInferenceSession
 import org.jetbrains.kotlin.fir.symbols.impl.FirAnonymousFunctionSymbol
 
 class FirSpecialTowerDataContexts {
-    private val towerDataContextForAnonymousFunctions: MutableMap<FirAnonymousFunctionSymbol, FirTowerDataContext> = mutableMapOf()
-    private val towerDataContextForCallableReferences: MutableMap<FirCallableReferenceAccess, FirTowerDataContext> = mutableMapOf()
+    private val contextForAnonymousFunctions: MutableMap<FirAnonymousFunctionSymbol, PostponedAtomsResolutionContext> = mutableMapOf()
+    private val contextForCallableReferences: MutableMap<FirCallableReferenceAccess, PostponedAtomsResolutionContext> = mutableMapOf()
 
-    fun getAnonymousFunctionContext(symbol: FirAnonymousFunctionSymbol): FirTowerDataContext? {
-        return towerDataContextForAnonymousFunctions[symbol]
+    fun getAnonymousFunctionContext(symbol: FirAnonymousFunctionSymbol): PostponedAtomsResolutionContext? {
+        return contextForAnonymousFunctions[symbol]
     }
 
-    fun getCallableReferenceContext(access: FirCallableReferenceAccess): FirTowerDataContext? {
-        return towerDataContextForCallableReferences[access]
+    fun getCallableReferenceContext(access: FirCallableReferenceAccess): PostponedAtomsResolutionContext? {
+        return contextForCallableReferences[access]
     }
 
-    fun storeAnonymousFunctionContext(symbol: FirAnonymousFunctionSymbol, context: FirTowerDataContext) {
-        towerDataContextForAnonymousFunctions[symbol] = context
+    fun storeAnonymousFunctionContext(
+        symbol: FirAnonymousFunctionSymbol,
+        context: FirTowerDataContext,
+        inferenceSession: FirInferenceSession,
+    ) {
+        contextForAnonymousFunctions[symbol] = Pair(context, inferenceSession)
     }
 
     fun dropAnonymousFunctionContext(symbol: FirAnonymousFunctionSymbol) {
-        towerDataContextForAnonymousFunctions.remove(symbol)
+        contextForAnonymousFunctions.remove(symbol)
     }
 
-    fun storeCallableReferenceContext(access: FirCallableReferenceAccess, context: FirTowerDataContext) {
-        towerDataContextForCallableReferences[access] = context
+    fun storeCallableReferenceContext(
+        access: FirCallableReferenceAccess,
+        context: FirTowerDataContext,
+        inferenceSession: FirInferenceSession
+    ) {
+        contextForCallableReferences[access] = Pair(context, inferenceSession)
     }
 
     fun dropCallableReferenceContext(access: FirCallableReferenceAccess) {
-        towerDataContextForCallableReferences.remove(access)
+        contextForCallableReferences.remove(access)
     }
 
     fun putAll(contexts: FirSpecialTowerDataContexts) {
-        towerDataContextForCallableReferences.putAll(contexts.towerDataContextForCallableReferences)
-        towerDataContextForAnonymousFunctions.putAll(contexts.towerDataContextForAnonymousFunctions)
+        contextForCallableReferences.putAll(contexts.contextForCallableReferences)
+        contextForAnonymousFunctions.putAll(contexts.contextForAnonymousFunctions)
     }
 
     fun clear() {
-        towerDataContextForAnonymousFunctions.clear()
-        towerDataContextForCallableReferences.clear()
+        contextForAnonymousFunctions.clear()
+        contextForCallableReferences.clear()
     }
 }
+
+typealias PostponedAtomsResolutionContext = Pair<FirTowerDataContext, FirInferenceSession>
\ No newline at end of file
diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/ResolutionMode.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/ResolutionMode.kt
index 8af883c..723dc4c 100644
--- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/ResolutionMode.kt
+++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/ResolutionMode.kt
@@ -28,7 +28,11 @@
     }
 
     data object ContextIndependent : ResolutionMode(forceFullCompletion = true)
-    data object ReceiverResolution : ResolutionMode(forceFullCompletion = true)
+
+    sealed class ReceiverResolution(val forCallableReference: Boolean) : ResolutionMode(forceFullCompletion = true) {
+        data object ForCallableReference : ReceiverResolution(forCallableReference = true)
+        companion object : ReceiverResolution(forCallableReference = false)
+    }
 
     class WithExpectedType(
         val expectedTypeRef: FirResolvedTypeRef,
diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/Arguments.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/Arguments.kt
index cff3f46..c058961 100644
--- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/Arguments.kt
+++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/Arguments.kt
@@ -7,10 +7,12 @@
 
 import org.jetbrains.kotlin.builtins.functions.isBasicFunctionOrKFunction
 import org.jetbrains.kotlin.config.LanguageFeature
-import org.jetbrains.kotlin.fir.*
+import org.jetbrains.kotlin.fir.FirSession
 import org.jetbrains.kotlin.fir.declarations.FirCallableDeclaration
 import org.jetbrains.kotlin.fir.declarations.FirValueParameter
 import org.jetbrains.kotlin.fir.expressions.*
+import org.jetbrains.kotlin.fir.languageVersionSettings
+import org.jetbrains.kotlin.fir.lookupTracker
 import org.jetbrains.kotlin.fir.references.FirResolvedNamedReference
 import org.jetbrains.kotlin.fir.references.toResolvedCallableSymbol
 import org.jetbrains.kotlin.fir.resolve.*
@@ -21,6 +23,7 @@
 import org.jetbrains.kotlin.fir.resolve.inference.preprocessLambdaArgument
 import org.jetbrains.kotlin.fir.resolve.transformers.ReturnTypeCalculator
 import org.jetbrains.kotlin.fir.resolve.transformers.ensureResolvedTypeDeclaration
+import org.jetbrains.kotlin.fir.returnExpressions
 import org.jetbrains.kotlin.fir.symbols.ConeClassLikeLookupTag
 import org.jetbrains.kotlin.fir.symbols.ConeTypeParameterLookupTag
 import org.jetbrains.kotlin.fir.types.*
@@ -34,6 +37,7 @@
 import org.jetbrains.kotlin.resolve.calls.inference.model.SimpleConstraintSystemConstraintPosition
 import org.jetbrains.kotlin.types.AbstractTypeChecker
 import org.jetbrains.kotlin.types.model.TypeSystemCommonSuperTypesContext
+import org.jetbrains.kotlin.types.model.safeSubstitute
 import org.jetbrains.kotlin.types.model.typeConstructor
 
 val SAM_LOOKUP_NAME = Name.special("<SAM-CONSTRUCTOR>")
@@ -190,7 +194,7 @@
      *   placeholder type with value 0, but argument contains type with proper literal value
      */
     val type: ConeKotlinType = context.returnTypeCalculator.tryCalculateReturnType(candidate.symbol.fir as FirCallableDeclaration).type
-    val argumentType = candidate.substitutor.substituteOrSelf(type)
+    val argumentType = candidate.substitutor.substituteOrSelf(type).prepareTypeForPartiallyCompletedPCLACall(candidate, csBuilder)
     resolvePlainArgumentType(
         csBuilder,
         argument,
@@ -204,6 +208,26 @@
     )
 }
 
+// For PCLA/Delegate inference partially completed calls, there might be a situation when
+// - There are already fixed variables (because we need them for lambda analysis)
+// - They are used in return types
+//
+// In this case, we substitute them explicitly because otherwise
+// TypeCheckerStateForConstraintInjector.fixedTypeVariable throws an exception.
+//
+// Note that this is not relevant outside PCLA context because
+// - For partial completion, we avoid fixing TVs that are used inside return types
+// - For FULL completion, we would run completion results writing, so there would be no candidates and type variables inside return types.
+//
+// See singleBranchConditionLastStatementInLambda.kt and assignmentUsingIncompletePCLACall.kt tests
+private fun ConeKotlinType.prepareTypeForPartiallyCompletedPCLACall(
+    candidate: Candidate, outerCSBuilder: ConstraintSystemBuilder
+): ConeKotlinType {
+    if (!candidate.system.usesOuterCs) return this
+
+    return outerCSBuilder.buildCurrentSubstitutor().safeSubstitute(candidate.callInfo.session.typeContext, this) as ConeKotlinType
+}
+
 fun Candidate.resolvePlainExpressionArgument(
     csBuilder: ConstraintSystemBuilder,
     argument: FirExpression,
diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/CallKind.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/CallKind.kt
index 88dbaf0..4537672 100644
--- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/CallKind.kt
+++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/CallKind.kt
@@ -24,6 +24,7 @@
         ConstraintSystemForks,
         CheckIncompatibleTypeVariableUpperBounds,
         TypeParameterAsCallable,
+        TypeVariablesInExplicitReceivers,
     )
 
     object SyntheticSelect : CallKind(
@@ -59,6 +60,7 @@
         ConstraintSystemForks,
         CheckIncompatibleTypeVariableUpperBounds,
         TypeParameterAsCallable,
+        TypeVariablesInExplicitReceivers,
     )
 
     object DelegatingConstructorCall : CallKind(
@@ -94,6 +96,7 @@
         CheckIncompatibleTypeVariableUpperBounds,
         ProcessDynamicExtensionAnnotation,
         LowerPriorityIfDynamic,
+        TypeVariablesInExplicitReceivers,
     )
 
     object SyntheticIdForCallableReferencesResolution : CallKind(
diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/Candidate.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/Candidate.kt
index 30304a4..7e794a6 100644
--- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/Candidate.kt
+++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/Candidate.kt
@@ -7,12 +7,12 @@
 
 import org.jetbrains.kotlin.KtFakeSourceElementKind
 import org.jetbrains.kotlin.fakeElement
+import org.jetbrains.kotlin.fir.declarations.FirAnonymousFunction
 import org.jetbrains.kotlin.fir.declarations.FirValueParameter
-import org.jetbrains.kotlin.fir.expressions.FirExpression
-import org.jetbrains.kotlin.fir.expressions.FirSmartCastExpression
-import org.jetbrains.kotlin.fir.expressions.FirThisReceiverExpression
+import org.jetbrains.kotlin.fir.expressions.*
 import org.jetbrains.kotlin.fir.expressions.builder.buildThisReceiverExpressionCopy
 import org.jetbrains.kotlin.fir.expressions.impl.FirExpressionStub
+import org.jetbrains.kotlin.fir.resolve.inference.FirInferenceSession
 import org.jetbrains.kotlin.fir.resolve.inference.InferenceComponents
 import org.jetbrains.kotlin.fir.resolve.inference.PostponedResolvedAtom
 import org.jetbrains.kotlin.fir.resolve.substitution.ConeSubstitutor
@@ -28,6 +28,7 @@
 import org.jetbrains.kotlin.resolve.calls.tower.CandidateApplicability
 import org.jetbrains.kotlin.resolve.calls.tower.isSuccess
 import org.jetbrains.kotlin.util.CodeFragmentAdjustment
+import org.jetbrains.kotlin.utils.addToStdlib.runUnless
 
 class Candidate(
     symbol: FirBasedSymbol<*>,
@@ -47,6 +48,7 @@
     val isFromCompanionObjectTypeScope: Boolean = false,
     // It's only true if we're in the member scope of smart cast receiver and this particular candidate came from original type
     val isFromOriginalTypeInPresenceOfSmartCast: Boolean = false,
+    inferenceSession: FirInferenceSession,
 ) : AbstractCandidate() {
 
     override var symbol: FirBasedSymbol<*> = symbol
@@ -66,10 +68,21 @@
         this.symbol = symbol
     }
 
+    val usedOuterCs: Boolean get() = system.usesOuterCs
+
     private var systemInitialized: Boolean = false
     val system: NewConstraintSystemImpl by lazy(LazyThreadSafetyMode.NONE) {
         val system = constraintSystemFactory.createConstraintSystem()
-        system.setBaseSystem(baseSystem)
+
+        val baseCSFromInferenceSession =
+            runUnless(baseSystem.usesOuterCs) { inferenceSession.baseConstraintStorageForCandidate(this) }
+        if (baseCSFromInferenceSession != null) {
+            system.setBaseSystem(baseCSFromInferenceSession)
+            system.addOtherSystem(baseSystem)
+        } else {
+            system.setBaseSystem(baseSystem)
+        }
+
         systemInitialized = true
         system
     }
@@ -102,6 +115,9 @@
     var functionTypesOfSamConversions: HashMap<FirExpression, ConeKotlinType>? = null
     lateinit var typeArgumentMapping: TypeArgumentMapping
     val postponedAtoms = mutableListOf<PostponedResolvedAtom>()
+    val postponedPCLACalls = mutableListOf<FirStatement>()
+    val lambdasAnalyzedWithPCLA = mutableListOf<FirAnonymousFunction>()
+    val onCompletionResultsWritingCallbacks = mutableListOf<(ConeSubstitutor) -> Unit>()
 
     var currentApplicability = CandidateApplicability.RESOLVED
         private set
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 fc39b97..41b85af 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
@@ -35,10 +35,26 @@
     companion object {
         private fun buildBaseSystem(context: ResolutionContext, callInfo: CallInfo): ConstraintStorage {
             val system = context.inferenceComponents.createConstraintSystem()
-            system.addOuterSystem(context.bodyResolveContext.outerConstraintStorage)
-            callInfo.arguments.forEach {
-                system.addSubsystemFromExpression(it)
+
+            val argumentSubsystems = buildList {
+                callInfo.arguments.forEach {
+                    processConstraintStorageFromExpression(it, this::add)
+                }
             }
+
+            // If any of the arguments uses outer CS, then all of them effectively share it, so we might add the last one as
+            // containing the most up-to-date version.
+            val lastSubsystemWithOuterCs = argumentSubsystems.lastOrNull { it.usesOuterCs }
+            if (lastSubsystemWithOuterCs != null) {
+                system.setBaseSystem(lastSubsystemWithOuterCs)
+            }
+
+            argumentSubsystems.forEach {
+                if (!it.usesOuterCs) {
+                    system.addOtherSystem(it)
+                }
+            }
+
             return system.asReadOnlyStorage()
         }
     }
@@ -79,6 +95,7 @@
                 ExplicitReceiverKind.NO_EXPLICIT_RECEIVER, ExplicitReceiverKind.BOTH_RECEIVERS -> false
             },
             isFromOriginalTypeInPresenceOfSmartCast,
+            context.bodyResolveContext.inferenceSession,
         )
 
         // The counterpart in FE 1.0 checks if the given descriptor is VariableDescriptor yet not PropertyDescriptor.
@@ -165,6 +182,7 @@
             baseSystem,
             callInfo,
             originScope = null,
+            inferenceSession = context.bodyResolveContext.inferenceSession,
         )
     }
 
@@ -194,25 +212,47 @@
     }
 }
 
-fun PostponedArgumentsAnalyzerContext.addSubsystemFromExpression(statement: FirStatement): Boolean {
+fun processConstraintStorageFromExpression(statement: FirStatement, processor: (ConstraintStorage) -> Unit): Boolean {
     return when (statement) {
         is FirQualifiedAccessExpression,
         is FirWhenExpression,
         is FirTryExpression,
         is FirCheckNotNullCall,
-        is FirElvisExpression -> {
+        is FirElvisExpression,
+        -> {
             val candidate = (statement as FirResolvable).candidate() ?: return false
-            addOtherSystem(candidate.system.asReadOnlyStorage())
+            processor(candidate.system.asReadOnlyStorage())
             true
         }
 
-        is FirSafeCallExpression -> addSubsystemFromExpression(statement.selector)
-        is FirWrappedArgumentExpression -> addSubsystemFromExpression(statement.expression)
-        is FirBlock -> statement.returnExpressions().any { addSubsystemFromExpression(it) }
+        is FirSafeCallExpression -> processConstraintStorageFromExpression(statement.selector, processor)
+        is FirWrappedArgumentExpression -> processConstraintStorageFromExpression(statement.expression, processor)
+        is FirBlock -> {
+            var wasAny = false
+
+            // Might be `.any {` call, but we should process all the items
+            statement.returnExpressions().forEach {
+                if (processConstraintStorageFromExpression(it, processor)) {
+                    wasAny = true
+                }
+            }
+
+            wasAny
+        }
         else -> false
     }
 }
 
+fun PostponedArgumentsAnalyzerContext.addSubsystemFromExpression(statement: FirStatement): Boolean {
+    return processConstraintStorageFromExpression(statement) {
+        // If a call inside a lambda uses outer CS,
+        // it's already integrated into inference session via FirPCLAInferenceSession.processPartiallyResolvedCall
+        if (!it.usesOuterCs) {
+            addOtherSystem(it)
+        }
+    }
+}
+
 internal fun FirResolvable.candidate(): Candidate? {
     return when (val callee = this.calleeReference) {
         is FirNamedReferenceWithCandidate -> return callee.candidate
diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/ResolutionStages.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/ResolutionStages.kt
index aad0a70..36fdcc1 100644
--- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/ResolutionStages.kt
+++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/ResolutionStages.kt
@@ -123,21 +123,6 @@
 
         candidate.chosenExtensionReceiver = receiver.expression
 
-        val checkBuilderInferenceRestriction =
-            !context.session.languageVersionSettings
-                .supportsFeature(LanguageFeature.NoBuilderInferenceWithoutAnnotationRestriction)
-        if (checkBuilderInferenceRestriction) {
-            val resolvedType = receiver.expression.resolvedType
-            if (resolvedType is ConeStubTypeForChainInference) {
-                val typeVariable = resolvedType.constructor.variable
-                sink.yieldDiagnostic(
-                    StubBuilderInferenceReceiver(
-                        (typeVariable.typeConstructor.originalTypeParameter as ConeTypeParameterLookupTag).typeParameterSymbol
-                    )
-                )
-            }
-        }
-
         sink.yieldIfNeed()
     }
 
@@ -264,6 +249,39 @@
     }
 }
 
+object TypeVariablesInExplicitReceivers : ResolutionStage() {
+    override suspend fun check(candidate: Candidate, callInfo: CallInfo, sink: CheckerSink, context: ResolutionContext) {
+        if (callInfo.callSite.isAnyOfDelegateOperators()) return
+
+        val explicitReceiver = callInfo.explicitReceiver ?: return checkOtherCases(candidate)
+
+        val typeVariableType = explicitReceiver.resolvedType.obtainTypeVariable() ?: return checkOtherCases(candidate)
+        val typeParameter =
+            (typeVariableType.typeConstructor.originalTypeParameter as? ConeTypeParameterLookupTag)?.typeParameterSymbol?.fir
+                ?: return checkOtherCases(candidate)
+
+        sink.reportDiagnostic(TypeVariableAsExplicitReceiver(explicitReceiver, typeParameter))
+    }
+
+    private fun checkOtherCases(candidate: Candidate) {
+        require(candidate.chosenExtensionReceiverExpression()?.resolvedType?.obtainTypeVariable() == null) {
+            "Found TV in extension receiver of $candidate"
+        }
+
+        require(candidate.dispatchReceiverExpression()?.resolvedType?.obtainTypeVariable() == null) {
+            "Found TV in extension receiver of $candidate"
+        }
+    }
+
+    private fun ConeKotlinType.obtainTypeVariable(): ConeTypeVariableType? = when (this) {
+        is ConeFlexibleType -> lowerBound.obtainTypeVariable()
+        is ConeTypeVariableType -> this
+        is ConeDefinitelyNotNullType -> original.obtainTypeVariable()
+        is ConeIntersectionType -> intersectedTypes.firstNotNullOfOrNull { it.obtainTypeVariable() }
+        else -> null
+    }
+}
+
 private fun Candidate.findClosestMatchingReceivers(
     expectedType: ConeKotlinType,
     receiverGroups: List<List<FirExpression>>,
@@ -569,7 +587,7 @@
             if (atom is ResolvedCallableReferenceAtom) {
                 val (applicability, success) =
                     context.bodyResolveComponents.callResolver.resolveCallableReference(
-                        candidate.csBuilder, atom, hasSyntheticOuterCall = candidate.callInfo.name == ACCEPT_SPECIFIC_TYPE.callableName
+                        candidate, atom, hasSyntheticOuterCall = candidate.callInfo.name == ACCEPT_SPECIFIC_TYPE.callableName
                     )
                 if (!success) {
                     // If the resolution was unsuccessful, we ensure that an error will be reported for the callable reference
diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/dfa/FirDataFlowAnalyzer.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/dfa/FirDataFlowAnalyzer.kt
index 3267f223..40016b4 100644
--- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/dfa/FirDataFlowAnalyzer.kt
+++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/dfa/FirDataFlowAnalyzer.kt
@@ -20,6 +20,7 @@
 import org.jetbrains.kotlin.fir.references.toResolvedPropertySymbol
 import org.jetbrains.kotlin.fir.resolve.*
 import org.jetbrains.kotlin.fir.resolve.calls.ImplicitReceiverValue
+import org.jetbrains.kotlin.fir.resolve.calls.candidate
 import org.jetbrains.kotlin.fir.resolve.dfa.cfg.*
 import org.jetbrains.kotlin.fir.resolve.substitution.ConeSubstitutor
 import org.jetbrains.kotlin.fir.resolve.substitution.ConeSubstitutorByMap
@@ -30,7 +31,10 @@
 import org.jetbrains.kotlin.fir.scopes.impl.toConeType
 import org.jetbrains.kotlin.fir.scopes.unsubstitutedScope
 import org.jetbrains.kotlin.fir.symbols.FirBasedSymbol
-import org.jetbrains.kotlin.fir.symbols.impl.*
+import org.jetbrains.kotlin.fir.symbols.impl.FirClassSymbol
+import org.jetbrains.kotlin.fir.symbols.impl.FirPropertySymbol
+import org.jetbrains.kotlin.fir.symbols.impl.FirRegularClassSymbol
+import org.jetbrains.kotlin.fir.symbols.impl.FirTypeParameterSymbol
 import org.jetbrains.kotlin.fir.types.*
 import org.jetbrains.kotlin.name.ClassId
 import org.jetbrains.kotlin.name.StandardClassIds
@@ -926,6 +930,10 @@
 
     private fun FirStatement.orderedArguments(callee: FirFunction): Array<out FirExpression?>? {
         fun FirQualifiedAccessExpression.firstReceiver(): FirExpression? {
+            val candidate = candidate()
+            if (candidate != null) {
+                return candidate.chosenExtensionReceiverExpression() ?: candidate.dispatchReceiverExpression()
+            }
             return extensionReceiver ?: dispatchReceiver
         }
 
@@ -937,7 +945,7 @@
 
         return when (this) {
             is FirFunctionCall -> {
-                val argumentToParameter = resolvedArgumentMapping ?: return null
+                val argumentToParameter = resolvedArgumentMapping ?: candidate()?.argumentMapping ?: return null
                 val parameterToArgument = argumentToParameter.entries.associate { it.value to it.key.unwrapArgument() }
                 Array(callee.valueParameters.size + 1) { i ->
                     if (i > 0) parameterToArgument[callee.valueParameters[i - 1]] else receiver
@@ -1154,7 +1162,9 @@
     }
 
     private fun exitBooleanNot(flow: MutableFlow, expression: FirFunctionCall) {
-        val argumentVariable = variableStorage.get(flow, expression.dispatchReceiver!!) ?: return
+        val argumentVariable = variableStorage.get(flow,
+            expression.candidate()?.dispatchReceiverExpression() ?: expression.dispatchReceiver!!
+        ) ?: return
         val expressionVariable = variableStorage.createSynthetic(expression)
         // Alternatively: (expression == true => argument == false) && (expression == false => argument == true)
         // Which implementation is faster and/or consumes less memory is an open question.
diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/inference/ConstraintSystemCompleter.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/inference/ConstraintSystemCompleter.kt
index add0736..8d8b659 100644
--- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/inference/ConstraintSystemCompleter.kt
+++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/inference/ConstraintSystemCompleter.kt
@@ -23,10 +23,7 @@
 import org.jetbrains.kotlin.fir.types.ConeErrorType
 import org.jetbrains.kotlin.fir.types.ConeKotlinType
 import org.jetbrains.kotlin.fir.types.ConeTypeVariable
-import org.jetbrains.kotlin.resolve.calls.inference.components.ConstraintSystemCompletionContext
-import org.jetbrains.kotlin.resolve.calls.inference.components.ConstraintSystemCompletionMode
-import org.jetbrains.kotlin.resolve.calls.inference.components.TypeVariableDependencyInformationProvider
-import org.jetbrains.kotlin.resolve.calls.inference.components.TypeVariableDirectionCalculator
+import org.jetbrains.kotlin.resolve.calls.inference.components.*
 import org.jetbrains.kotlin.resolve.calls.inference.model.NewConstraintSystemImpl
 import org.jetbrains.kotlin.resolve.calls.inference.model.NotEnoughInformationForTypeParameter
 import org.jetbrains.kotlin.resolve.calls.inference.model.VariableWithConstraints
@@ -81,9 +78,9 @@
             if (analyzeArgumentWithFixedParameterTypes(languageVersionSettings, postponedArguments, analyze))
                 continue
 
-            val isThereAnyReadyForFixationVariable = variableFixationFinder.findFirstVariableForFixation(
-                this,
-                getOrderedAllTypeVariables(collectVariablesFromContext, topLevelAtoms),
+            val isThereAnyReadyForFixationVariable = findFirstVariableForFixation(
+                collectVariablesFromContext,
+                topLevelAtoms,
                 postponedArguments,
                 completionMode,
                 topLevelType
@@ -113,7 +110,7 @@
             if (wasBuiltNewExpectedTypeForSomeArgument)
                 continue
 
-            if (completionMode == ConstraintSystemCompletionMode.FULL) {
+            if (completionMode.allLambdasShouldBeAnalyzed) {
                 // Stage 3: fix variables for parameter types of all postponed arguments
                 for (argument in postponedArguments) {
                     val variableWasFixed = postponedArgumentsInputTypesResolver.fixNextReadyVariableForParameterTypeIfNeeded(
@@ -152,21 +149,32 @@
                 continue
 
             // Stage 7: try to complete call with the builder inference if there are uninferred type variables
-            val allTypeVariables = getOrderedAllTypeVariables(collectVariablesFromContext, topLevelAtoms)
             val areThereAppearedProperConstraintsForSomeVariable = tryToCompleteWithBuilderInference(
-                completionMode, topLevelType, postponedArguments, allTypeVariables, analyze
+                completionMode, postponedArguments, analyze,
             )
 
             if (areThereAppearedProperConstraintsForSomeVariable)
                 continue
 
+            if (completionMode == ConstraintSystemCompletionMode.PCLA_POSTPONED_CALL) {
+                // Complete all lambdas, maybe with semi-fixing type variables used as top-level input types
+                if (analyzeRemainingNotAnalyzedPostponedArgument(postponedArguments, analyze))
+                    continue
+            }
+
             // Stage 8: report "not enough information" for uninferred type variables
             reportNotEnoughTypeInformation(
-                completionMode, topLevelAtoms, topLevelType, allTypeVariables, postponedArguments
+                completionMode, topLevelAtoms, topLevelType, postponedArguments
             )
 
+            if (completionMode == ConstraintSystemCompletionMode.PCLA_POSTPONED_CALL) {
+                reportNotEnoughInformationForTypeVariablesRequiredForInputTypesOfLambdas(
+                    postponedArguments, topLevelType, dependencyProvider, topLevelAtoms
+                )
+            }
+
             // Stage 9: force analysis of remaining not analyzed postponed arguments and rerun stages if there are
-            if (completionMode == ConstraintSystemCompletionMode.FULL) {
+            if (completionMode.allLambdasShouldBeAnalyzed) {
                 if (analyzeRemainingNotAnalyzedPostponedArgument(postponedArguments, analyze))
                     continue
             }
@@ -175,6 +183,49 @@
         }
     }
 
+    private fun ConstraintSystemCompletionContext.reportNotEnoughInformationForTypeVariablesRequiredForInputTypesOfLambdas(
+        postponedArguments: List<PostponedResolvedAtom>,
+        topLevelType: ConeKotlinType,
+        dependencyProvider: TypeVariableDependencyInformationProvider,
+        topLevelAtoms: List<FirStatement>,
+    ) {
+        for (argument in postponedArguments) {
+            val variableForFixation = postponedArgumentsInputTypesResolver.findNextVariableForReportingNotInferredInputType(
+                this,
+                argument,
+                postponedArguments,
+                topLevelType,
+                dependencyProvider,
+            ) ?: continue
+
+            assert(!variableForFixation.isReady) {
+                "At this stage there should be no remaining variables with proper constraints from input types"
+            }
+
+            val variableWithConstraints = notFixedTypeVariables.getValue(variableForFixation.variable)
+            processVariableWhenNotEnoughInformation(variableWithConstraints, topLevelAtoms)
+        }
+    }
+
+    private fun ConstraintSystemCompletionContext.findFirstVariableForFixation(
+        collectVariablesFromContext: Boolean,
+        topLevelAtoms: List<FirStatement>,
+        postponedArguments: List<PostponedResolvedAtom>,
+        completionMode: ConstraintSystemCompletionMode,
+        topLevelType: ConeKotlinType,
+    ): VariableFixationFinder.VariableForFixation? {
+        return variableFixationFinder.findFirstVariableForFixation(
+            this,
+            getOrderedAllTypeVariables(
+                collectVariablesFromContext,
+                topLevelAtoms
+            ),
+            postponedArguments,
+            completionMode,
+            topLevelType
+        )
+    }
+
     /**
      * General documentation for builder inference algorithm is located at `/docs/fir/builder_inference.md`
      *
@@ -183,12 +234,10 @@
      */
     private fun ConstraintSystemCompletionContext.tryToCompleteWithBuilderInference(
         completionMode: ConstraintSystemCompletionMode,
-        topLevelType: ConeKotlinType,
         postponedArguments: List<PostponedResolvedAtom>,
-        allTypeVariables: List<TypeConstructorMarker>,
         analyze: (PostponedResolvedAtom) -> Unit,
     ): Boolean {
-        if (completionMode != ConstraintSystemCompletionMode.FULL) return false
+        if (!completionMode.allLambdasShouldBeAnalyzed) return false
 
         // If we use the builder inference anyway (if the annotation is presented), then we are already analysed builder inference lambdas
         if (!languageVersionSettings.supportsFeature(LanguageFeature.UseBuilderInferenceOnlyIfNeeded)) return false
@@ -198,56 +247,17 @@
         fun ResolvedLambdaAtom.notFixedInputTypeVariables(): List<TypeVariableTypeConstructorMarker> =
             inputTypes.flatMap { it.extractTypeVariables() }.filter { it !in fixedTypeVariables }
 
-        val checkForDangerousBuilderInference =
-            !languageVersionSettings.supportsFeature(LanguageFeature.NoBuilderInferenceWithoutAnnotationRestriction)
-
-        // Let's call builder lambda (BL) a lambda that has non-zero not fixed input type variables in
-        // type arguments of it's input types
-        // ex: MutableList<T>.() -> Unit
-        // During type inference of call-site such lambda will be considered BL, if
-        // T not fixed yet
-        // Given we have two or more builder lambdas among postponed arguments, it could result in incorrect type inference due to
-        // incorrect constraint propagation into common system
-        // See KT-53740
-        // Constraint propagation into common system happens at
-        // org.jetbrains.kotlin.resolve.calls.components.PostponedArgumentsAnalyzer.applyResultsOfAnalyzedLambdaToCandidateSystem
-        val dangerousBuilderInferenceWithoutAnnotation =
-            lambdaArguments.size >= 2 && lambdaArguments.count { it.notFixedInputTypeVariables().isNotEmpty() } >= 2
-
-        // We assume useBuilderInferenceWithoutAnnotation = true for FIR
-
-        val builder = getBuilder()
+        var anyAnalyzed = false
         for (argument in lambdaArguments) {
-            val reallyHasBuilderInferenceAnnotation = argument.isCorrespondingParameterAnnotatedWithBuilderInference
-
             val notFixedInputTypeVariables = argument.notFixedInputTypeVariables()
 
             if (notFixedInputTypeVariables.isEmpty()) continue
-
-            // we have dangerous inference situation
-            // if lambda annotated with BuilderInference it's probably safe, due to type shape
-            // otherwise report multi-lambda builder inference restriction diagnostic
-            if (checkForDangerousBuilderInference && dangerousBuilderInferenceWithoutAnnotation && !reallyHasBuilderInferenceAnnotation) {
-                for (variable in notFixedInputTypeVariables) {
-                    addError(AnonymousFunctionBasedMultiLambdaBuilderInferenceRestriction(argument.atom, variable.typeParameter!!))
-                }
-            }
-
-            for (variable in notFixedInputTypeVariables) {
-                builder.markPostponedVariable(notFixedTypeVariables.getValue(variable).typeVariable)
-            }
-
             analyze(argument)
+
+            anyAnalyzed = true
         }
 
-        val variableForFixation = variableFixationFinder.findFirstVariableForFixation(
-            this, allTypeVariables, postponedArguments, completionMode, topLevelType
-        )
-
-        // continue completion (rerun stages) only if ready for fixation variables with proper constraints have appeared
-        // (after analysing a lambda with the builder inference)
-        // otherwise we don't continue and report "not enough type information" error
-        return variableForFixation?.hasProperConstraint == true
+        return anyAnalyzed
     }
 
     private fun transformToAtomWithNewFunctionExpectedType(
@@ -277,16 +287,12 @@
         collectVariablesFromContext: Boolean,
         postponedArguments: List<PostponedResolvedAtom>,
     ): Boolean {
-        val variableForFixation = variableFixationFinder.findFirstVariableForFixation(
-            this,
-            getOrderedAllTypeVariables(collectVariablesFromContext, topLevelAtoms),
-            postponedArguments,
-            completionMode,
-            topLevelType
+        val variableForFixation = findFirstVariableForFixation(
+            collectVariablesFromContext, topLevelAtoms, postponedArguments, completionMode, topLevelType
         ) ?: return false
 
         val variableWithConstraints = notFixedTypeVariables.getValue(variableForFixation.variable)
-        if (!variableForFixation.hasProperConstraint) return false
+        if (!variableForFixation.isReady) return false
 
         fixVariable(this, variableWithConstraints)
 
@@ -297,19 +303,17 @@
         completionMode: ConstraintSystemCompletionMode,
         topLevelAtoms: List<FirStatement>,
         topLevelType: ConeKotlinType,
-        allTypeVariables: List<TypeConstructorMarker>,
         postponedArguments: List<PostponedResolvedAtom>,
     ) {
         while (true) {
-            val variableForFixation = variableFixationFinder.findFirstVariableForFixation(
-                this, allTypeVariables, postponedArguments, completionMode, topLevelType,
-            ) ?: break
-
-            assert(!variableForFixation.hasProperConstraint) {
+            val variableForFixation =
+                findFirstVariableForFixation(false, topLevelAtoms, postponedArguments, completionMode, topLevelType)
+                    ?: break
+            assert(!variableForFixation.isReady) {
                 "At this stage there should be no remaining variables with proper constraints"
             }
 
-            if (completionMode == ConstraintSystemCompletionMode.PARTIAL) break
+            if (completionMode != ConstraintSystemCompletionMode.FULL) break
 
             val variableWithConstraints = notFixedTypeVariables.getValue(variableForFixation.variable)
             processVariableWhenNotEnoughInformation(variableWithConstraints, topLevelAtoms)
@@ -399,30 +403,9 @@
             topLevelAtom.collectAllTypeVariables()
         }
 
-        checkNotFixedTypeVariablesCountConsistency(result)
-
         return result.toList()
     }
 
-    private fun ConstraintSystemCompletionContext.checkNotFixedTypeVariablesCountConsistency(
-        result: LinkedHashSet<TypeConstructorMarker>,
-    ) {
-        val notFixedTypeVariablesToUse =
-            when (outerSystemVariablesPrefixSize) {
-                0 -> notFixedTypeVariables.keys
-                else -> notFixedTypeVariables.keys.toMutableSet().apply {
-                    removeAll(allTypeVariables.keys.take(outerSystemVariablesPrefixSize).toSet())
-                }
-            }
-
-        require(result.size == notFixedTypeVariablesToUse.size) {
-            val notFoundTypeVariables = notFixedTypeVariablesToUse.toMutableSet().apply {
-                removeAll(result)
-            }
-            "Not all type variables found: $notFoundTypeVariables"
-        }
-    }
-
     private fun fixVariable(
         c: ConstraintSystemCompletionContext,
         variableWithConstraints: VariableWithConstraints,
@@ -581,13 +564,21 @@
     val candidate = (calleeReference as? FirNamedReferenceWithCandidate)?.candidate ?: return
     processor(candidate)
 
+    val visited = mutableSetOf<FirStatement>()
+
     for (atom in candidate.postponedAtoms) {
         if (atom !is ResolvedLambdaAtom || !atom.analyzed) continue
 
         atom.returnStatements.forEach {
+            visited += it
             it.processAllContainingCallCandidates(processBlocks, processor)
         }
     }
+
+    for (call in candidate.postponedPCLACalls) {
+        if (!visited.add(call)) continue
+        call.processAllContainingCallCandidates(processBlocks, processor)
+    }
 }
 
 val Candidate.csBuilder: NewConstraintSystemImpl get() = system.getBuilder()
diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/inference/FirBuilderInferenceSession.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/inference/FirBuilderInferenceSession.kt
deleted file mode 100644
index 45b0ad4..0000000
--- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/inference/FirBuilderInferenceSession.kt
+++ /dev/null
@@ -1,388 +0,0 @@
-/*
- * Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
- * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
- */
-
-package org.jetbrains.kotlin.fir.resolve.inference
-
-import org.jetbrains.kotlin.config.LanguageFeature
-import org.jetbrains.kotlin.fir.FirElement
-import org.jetbrains.kotlin.fir.FirSession
-import org.jetbrains.kotlin.fir.declarations.FirAnonymousFunction
-import org.jetbrains.kotlin.fir.declarations.FirDeclaration
-import org.jetbrains.kotlin.fir.declarations.hasAnnotation
-import org.jetbrains.kotlin.fir.expressions.*
-import org.jetbrains.kotlin.fir.languageVersionSettings
-import org.jetbrains.kotlin.fir.resolve.calls.Candidate
-import org.jetbrains.kotlin.fir.resolve.calls.ImplicitExtensionReceiverValue
-import org.jetbrains.kotlin.fir.resolve.calls.ResolutionContext
-import org.jetbrains.kotlin.fir.resolve.calls.candidate
-import org.jetbrains.kotlin.fir.resolve.inference.model.ConeBuilderInferenceSubstitutionConstraintPosition
-import org.jetbrains.kotlin.fir.resolve.substitution.AbstractConeSubstitutor
-import org.jetbrains.kotlin.fir.resolve.substitution.ChainedSubstitutor
-import org.jetbrains.kotlin.fir.resolve.substitution.ConeSubstitutor
-import org.jetbrains.kotlin.fir.resolve.substitution.replaceStubsAndTypeVariablesToErrors
-import org.jetbrains.kotlin.fir.types.*
-import org.jetbrains.kotlin.fir.visitors.FirDefaultTransformer
-import org.jetbrains.kotlin.fir.visitors.transformSingle
-import org.jetbrains.kotlin.name.ClassId
-import org.jetbrains.kotlin.resolve.calls.inference.buildAbstractResultingSubstitutor
-import org.jetbrains.kotlin.resolve.calls.inference.model.*
-import org.jetbrains.kotlin.resolve.calls.inference.registerTypeVariableIfNotPresent
-import org.jetbrains.kotlin.resolve.descriptorUtil.BUILDER_INFERENCE_ANNOTATION_FQ_NAME
-import org.jetbrains.kotlin.types.model.TypeConstructorMarker
-import org.jetbrains.kotlin.types.model.TypeSubstitutorMarker
-import org.jetbrains.kotlin.types.model.safeSubstitute
-
-/**
- * General documentation for builder inference algorithm is located at `/docs/fir/builder_inference.md`
- */
-class FirBuilderInferenceSession(
-    private val lambda: FirAnonymousFunction,
-    resolutionContext: ResolutionContext,
-    private val stubsForPostponedVariables: Map<ConeTypeVariable, ConeStubType>,
-) : FirInferenceSessionForChainedResolve(resolutionContext) {
-    private val session = resolutionContext.session
-    private val commonCalls: MutableList<Pair<FirStatement, Candidate>> = mutableListOf()
-    private var lambdaImplicitReceivers: MutableList<ImplicitExtensionReceiverValue> = mutableListOf()
-
-    override fun <T> shouldRunCompletion(call: T): Boolean where T : FirResolvable, T : FirStatement {
-        val candidate = call.candidate
-        val system = candidate.system
-
-        if (system.hasContradiction) return true
-        if (!candidate.isSuitableForBuilderInference()) return true
-
-
-        val storage = system.getBuilder().currentStorage()
-
-        if (call.hasPostponed()) return true
-
-        return storage.notFixedTypeVariables.keys.all {
-            val variable = storage.allTypeVariables[it]
-            val isPostponed = variable != null && variable in storage.postponedTypeVariables
-            isPostponed || components.callCompleter.completer.variableFixationFinder.isTypeVariableHasProperConstraint(system, it)
-        }
-    }
-
-    private fun Candidate.isSuitableForBuilderInference(): Boolean {
-        val extensionReceiver = chosenExtensionReceiver
-        val dispatchReceiver = dispatchReceiver
-        return when {
-            extensionReceiver == null && dispatchReceiver == null -> false
-            dispatchReceiver?.resolvedType?.containsStubType() == true -> true
-            extensionReceiver?.resolvedType?.containsStubType() == true -> symbol.fir.hasBuilderInferenceAnnotation(session)
-            else -> false
-        }
-    }
-
-    private fun ConeKotlinType.containsStubType(): Boolean {
-        return this.contains {
-            it is ConeStubTypeForChainInference
-        }
-    }
-
-    private fun FirStatement.hasPostponed(): Boolean {
-        var result = false
-        processAllContainingCallCandidates(processBlocks = false) {
-            result = result || it.hasPostponed()
-        }
-        return result
-    }
-
-    private fun Candidate.hasPostponed(): Boolean {
-        return postponedAtoms.any { !it.analyzed }
-    }
-
-    fun addLambdaImplicitReceiver(receiver: ImplicitExtensionReceiverValue) {
-        lambdaImplicitReceivers += receiver
-    }
-
-    override fun <T> addCompletedCall(call: T, candidate: Candidate) where T : FirResolvable, T : FirStatement {
-        if (skipCall(call)) return
-        commonCalls += call to candidate
-    }
-
-    @Suppress("UNUSED_PARAMETER")
-    private fun <T> skipCall(call: T): Boolean where T : FirResolvable, T : FirStatement {
-        // TODO: what is FIR analog?
-        // if (descriptor is FakeCallableDescriptorForObject) return true
-        // if (!DescriptorUtils.isObject(descriptor) && isInLHSOfDoubleColonExpression(callInfo)) return true
-
-        return false
-    }
-
-    private fun buildCommonSystem(initialStorage: ConstraintStorage): Pair<NewConstraintSystemImpl, Boolean> {
-        // TODO(KT-64034): Missing handling of parent builder inference sessions
-        //  - See [org.jetbrains.kotlin.resolve.calls.inference.BuilderInferenceSession.initializeCommonSystem]
-
-        val commonSystem = components.session.inferenceComponents.createConstraintSystem()
-        val nonFixedToVariablesSubstitutor = createNonFixedTypeToVariableSubstitutor()
-
-        var effectivelyEmptyCommonSystem =
-            !integrateConstraints(commonSystem, initialStorage, nonFixedToVariablesSubstitutor, false)
-
-        for ((_, candidate) in commonCalls) {
-            val hasConstraints =
-                integrateConstraints(commonSystem, candidate.system.asReadOnlyStorage(), nonFixedToVariablesSubstitutor, false)
-            if (hasConstraints) effectivelyEmptyCommonSystem = false
-        }
-        for ((_, candidate) in partiallyResolvedCalls) {
-            val hasConstraints =
-                integrateConstraints(commonSystem, candidate.system.asReadOnlyStorage(), nonFixedToVariablesSubstitutor, true)
-            if (hasConstraints) effectivelyEmptyCommonSystem = false
-        }
-
-        return commonSystem to effectivelyEmptyCommonSystem
-    }
-
-    private fun createNonFixedTypeToVariableSubstitutor(): ConeSubstitutor {
-        val typeContext = components.session.typeContext
-
-        val bindings = mutableMapOf<TypeConstructorMarker, ConeKotlinType>()
-        for ((variable, nonFixedType) in stubsForPostponedVariables) {
-            bindings[nonFixedType.constructor] = variable.defaultType
-        }
-
-        return typeContext.typeSubstitutorByTypeConstructor(bindings)
-    }
-
-    private fun integrateConstraints(
-        commonSystem: NewConstraintSystemImpl,
-        storage: ConstraintStorage,
-        nonFixedToVariablesSubstitutor: ConeSubstitutor,
-        shouldIntegrateAllConstraints: Boolean
-    ): Boolean {
-        storage.notFixedTypeVariables.values.forEach { commonSystem.registerTypeVariableIfNotPresent(it.typeVariable) }
-
-        /*
-        * storage can contain the following substitutions:
-        *  TypeVariable(A) -> ProperType
-        *  TypeVariable(B) -> Special-Non-Fixed-Type
-        *
-        * while substitutor from parameter map non-fixed types to the original type variable
-        * */
-        val callSubstitutor =
-            storage.buildAbstractResultingSubstitutor(commonSystem, transformTypeVariablesToErrorTypes = false) as ConeSubstitutor
-
-        var introducedConstraint = false
-
-        for (initialConstraint in storage.initialConstraints) {
-            if (initialConstraint.position is BuilderInferencePosition) continue
-            if (integrateConstraintToSystem(
-                    commonSystem, initialConstraint, callSubstitutor, nonFixedToVariablesSubstitutor
-                )
-            ) {
-                introducedConstraint = true
-            }
-        }
-
-        if (shouldIntegrateAllConstraints) {
-            for ((variableConstructor, type) in storage.fixedTypeVariables) {
-                val typeVariable = storage.allTypeVariables.getValue(variableConstructor)
-                commonSystem.registerTypeVariableIfNotPresent(typeVariable)
-                commonSystem.addEqualityConstraint((typeVariable as ConeTypeVariable).defaultType, type, BuilderInferencePosition)
-                introducedConstraint = true
-            }
-        }
-
-        return introducedConstraint
-    }
-
-    private fun extractCommonCapturedTypes(lower: ConeKotlinType, upper: ConeKotlinType): List<ConeCapturedType> {
-        val extractedCapturedTypes = mutableSetOf<ConeCapturedType>().also { extractCapturedTypesTo(lower, it) }
-        return extractedCapturedTypes.filter { capturedType ->
-            upper.contains { it is ConeCapturedType && it.constructor === capturedType.constructor }
-        }
-    }
-
-    private fun extractCapturedTypesTo(type: ConeKotlinType, to: MutableSet<ConeCapturedType>) {
-        if (type is ConeCapturedType) {
-            to.add(type)
-        }
-        for (typeArgument in type.typeArguments) {
-            if (typeArgument !is ConeKotlinTypeProjection) continue
-            extractCapturedTypesTo(typeArgument.type, to)
-        }
-    }
-
-    private fun getResultingSubstitutor(commonSystem: NewConstraintSystemImpl): ConeSubstitutor {
-        val nonFixedToVariablesSubstitutor = createNonFixedTypeToVariableSubstitutor()
-        val commonSystemSubstitutor = commonSystem.buildCurrentSubstitutor() as ConeSubstitutor
-        return ChainedSubstitutor(nonFixedToVariablesSubstitutor, commonSystemSubstitutor)
-            .replaceStubsAndTypeVariablesToErrors(resolutionContext.typeContext, stubsForPostponedVariables.values.map { it.constructor })
-    }
-
-    private fun updateCalls(substitutor: ConeSubstitutor) {
-        val stubTypeSubstitutor = FirStubTypeTransformer(substitutor)
-        lambda.transformSingle(stubTypeSubstitutor, null)
-
-        // TODO: Builder inference should not modify implicit receivers. KT-54708
-        for (receiver in lambdaImplicitReceivers) {
-            @Suppress("DEPRECATION_ERROR")
-            receiver.updateTypeInBuilderInference(substitutor.substituteOrSelf(receiver.type))
-        }
-
-        val completionResultsWriter = components.callCompleter.createCompletionResultsWriter(substitutor)
-        for ((call, _) in partiallyResolvedCalls) {
-            call.transformSingle(completionResultsWriter, null)
-        }
-    }
-
-    private fun integrateConstraintToSystem(
-        commonSystem: NewConstraintSystemImpl,
-        initialConstraint: InitialConstraint,
-        callSubstitutor: ConeSubstitutor,
-        nonFixedToVariablesSubstitutor: ConeSubstitutor,
-    ): Boolean {
-        val substitutedConstraintWith =
-            initialConstraint.substitute(callSubstitutor).substituteNonFixedToVariables(nonFixedToVariablesSubstitutor)
-
-        val substitutedA = substitutedConstraintWith.a
-        val substitutedB = substitutedConstraintWith.b
-
-        if (commonSystem.isProperType(substitutedA) && (substitutedA == substitutedB || commonSystem.isProperType(substitutedB))) return false
-
-        val position = substitutedConstraintWith.position
-        when (initialConstraint.constraintKind) {
-            ConstraintKind.LOWER -> error("LOWER constraint shouldn't be used, please use UPPER")
-
-            ConstraintKind.UPPER -> commonSystem.addSubtypeConstraint(substitutedA, substitutedB, position)
-
-            ConstraintKind.EQUALITY ->
-                with(commonSystem) {
-                    addSubtypeConstraint(substitutedA, substitutedB, position)
-                    addSubtypeConstraint(substitutedB, substitutedA, position)
-                }
-        }
-        return true
-    }
-
-    private fun InitialConstraint.substitute(substitutor: TypeSubstitutorMarker): InitialConstraint {
-        val substitutedA = substitutor.safeSubstitute(resolutionContext.typeContext, this.a)
-        val substitutedB = substitutor.safeSubstitute(resolutionContext.typeContext, this.b)
-
-        if (substitutedA == a && substitutedB == b) return this
-
-        // TODO(KT-64033): Missing check for ForbidInferringPostponedTypeVariableIntoDeclaredUpperBound language feature
-
-        return InitialConstraint(
-            substitutedA,
-            substitutedB,
-            this.constraintKind,
-            ConeBuilderInferenceSubstitutionConstraintPosition(this)
-        )
-    }
-
-    /**
-     * This function substitutes stub types in constraint with the corresponding type variable
-     * Please make sure to pass correct {Stub(Tv) => Tv} substitutor
-     *
-     * E.g.:
-     * ```
-     * nonFixedToVariablesSubstitutor = {Stub(Tv) => Tv, Stub(Ov) => Ov}
-     * constraint: A<Stub(Tv)> <: B<Stub(Ov)> -> A<Tv> <: B<Ov>
-     * ```
-     *
-     * The main reason for this function's existence is the custom handling of captured types.
-     * See KT-64027
-     *
-     * @return Constraint, substituted according to the [nonFixedToVariablesSubstitutor]
-     */
-    private fun InitialConstraint.substituteNonFixedToVariables(
-        nonFixedToVariablesSubstitutor: ConeSubstitutor
-    ): InitialConstraint {
-        require(constraintKind != ConstraintKind.LOWER)
-
-        // TODO(KT-63996, KT-64027): This function assumes types passed in lower, upper order which isn't true for equality constraints
-        val commonCapTypes = extractCommonCapturedTypes(lower = a as ConeKotlinType, upper = b as ConeKotlinType)
-
-        // TODO(KT-64027): This logic tries to work-around the problem with type substitution consistency in captured types
-        //  In order to preserve consistency we collect captured types from both a and b and substitute them collectively
-        //  E.g:
-        //  substitutor = { B => W }
-        //  a = C<CapturedType(out B)_0>, b = D<CapturedType(out B)_0>
-        //  commonCapTypes = [CapturedType(out B)_0]
-        //  capTypesSubstitutor = { CapturedTypeConstructor_0 => CapturedType(out W)_1 }
-        //  substitutedA = C<CapturedType(out W)_1>
-        //  substitutedB = D<CapturedType(out W)_1>
-        val substitutedCommonCapType = commonCapTypes.associate {
-            it.constructor to nonFixedToVariablesSubstitutor.substituteOrSelf(it)
-        }
-
-        val capTypesSubstitutor = object : AbstractConeSubstitutor(resolutionContext.typeContext) {
-            override fun substituteType(type: ConeKotlinType): ConeKotlinType? {
-                if (type !is ConeCapturedType) return null
-                return substitutedCommonCapType[type.constructor]
-            }
-        }
-
-        val substitutedA = nonFixedToVariablesSubstitutor.safeSubstitute(
-            resolutionContext.typeContext,
-            capTypesSubstitutor.safeSubstitute(resolutionContext.typeContext, this.a)
-        )
-        val substitutedB = nonFixedToVariablesSubstitutor.safeSubstitute(
-            resolutionContext.typeContext,
-            capTypesSubstitutor.safeSubstitute(resolutionContext.typeContext, this.b)
-        )
-
-        return InitialConstraint(
-            substitutedA,
-            substitutedB,
-            constraintKind,
-            position
-        )
-    }
-}
-
-class FirStubTypeTransformer(private val substitutor: ConeSubstitutor) : FirDefaultTransformer<Nothing?>() {
-
-    override fun <E : FirElement> transformElement(element: E, data: Nothing?): E {
-        // All resolvable nodes should be implemented separately to cover substitution of receivers in the candidate
-        if (element is FirResolvable) {
-            element.candidate()?.let { processCandidate(it) }
-        }
-
-        // Since FirExpressions don't have typeRefs, they need to be updated separately.
-        // FirAnonymousFunctionExpression doesn't support replacing the type
-        // since it delegates the getter to the underlying FirAnonymousFunction.
-        if (element is FirExpression && element !is FirAnonymousFunctionExpression) {
-            // TODO Check why some expressions have unresolved type in builder inference session KT-61835
-            @OptIn(UnresolvedExpressionTypeAccess::class)
-            element.coneTypeOrNull
-                ?.let(substitutor::substituteOrNull)
-                ?.let { element.replaceConeTypeOrNull(it) }
-        }
-
-        @Suppress("UNCHECKED_CAST")
-        return element.transformChildren(this, data = null) as E
-    }
-
-    override fun transformTypeOperatorCall(typeOperatorCall: FirTypeOperatorCall, data: Nothing?): FirStatement {
-        if (typeOperatorCall.argument.resolvedType is ConeStubType) {
-            typeOperatorCall.replaceArgFromStubType(true)
-        }
-        return super.transformTypeOperatorCall(typeOperatorCall, data)
-    }
-
-    override fun transformResolvedTypeRef(resolvedTypeRef: FirResolvedTypeRef, data: Nothing?): FirTypeRef =
-        substitutor.substituteOrNull(resolvedTypeRef.type)?.let {
-            resolvedTypeRef.withReplacedConeType(it)
-        } ?: resolvedTypeRef
-
-    /*
-     * We should manually update all receivers in the all not completed candidates, because not all calls with candidates
-     *   contained in partiallyResolvedCalls and candidate stores not receiver values, which are updated, (TODO: remove this comment after removal of updating values)
-     *   and receivers of candidates are not direct FIR children of calls, so they won't be visited during regular transformChildren
-     */
-    private fun processCandidate(candidate: Candidate) {
-        candidate.dispatchReceiver = candidate.dispatchReceiver?.transform(this, data = null)
-        candidate.chosenExtensionReceiver = candidate.chosenExtensionReceiver?.transform(this, data = null)
-        candidate.contextReceiverArguments = candidate.contextReceiverArguments?.map { it.transform(this, data = null) }
-    }
-}
-
-private val BUILDER_INFERENCE_ANNOTATION_CLASS_ID: ClassId = ClassId.topLevel(BUILDER_INFERENCE_ANNOTATION_FQ_NAME)
-
-fun FirDeclaration.hasBuilderInferenceAnnotation(session: FirSession): Boolean =
-    hasAnnotation(BUILDER_INFERENCE_ANNOTATION_CLASS_ID, session)
diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/inference/FirCallCompleter.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/inference/FirCallCompleter.kt
index 7b2489d..24ca31d 100644
--- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/inference/FirCallCompleter.kt
+++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/inference/FirCallCompleter.kt
@@ -40,9 +40,8 @@
 import org.jetbrains.kotlin.resolve.calls.inference.addSubtypeConstraintIfCompatible
 import org.jetbrains.kotlin.resolve.calls.inference.buildAbstractResultingSubstitutor
 import org.jetbrains.kotlin.resolve.calls.inference.components.ConstraintSystemCompletionMode
+import org.jetbrains.kotlin.resolve.calls.inference.model.ConstraintStorage
 import org.jetbrains.kotlin.types.TypeApproximatorConfiguration
-import org.jetbrains.kotlin.types.model.StubTypeMarker
-import org.jetbrains.kotlin.types.model.TypeVariableMarker
 import org.jetbrains.kotlin.types.model.safeSubstitute
 import org.jetbrains.kotlin.utils.addToStdlib.runIf
 
@@ -80,18 +79,11 @@
         val completionMode = candidate.computeCompletionMode(
             session.inferenceComponents, resolutionMode, initialType
         ).let {
-            // The difference between `shouldAvoidFullCompletion` and `!shouldRunCompletion` is very subtle:
-            // we don't run even partial completion for `!inferenceSession.shouldRunCompletion(call)`, while actually
-            // do that for `shouldAvoidFullCompletion`
-            //
-            // As for implementations, `shouldRunCompletion` only works for Builder inference, while `shouldAvoidFullCompletion` is for
-            // delegate inference where it's assumed to have partially completed intermediate calls.
-            //
-            // Ideally, we should get rid of `shouldRunCompletion` once Builder inference is rewritten (see KT-61041 for tracking)
-            if (it == ConstraintSystemCompletionMode.FULL && inferenceSession.shouldAvoidFullCompletion(call))
-                ConstraintSystemCompletionMode.PARTIAL
-            else
-                it
+            when {
+                it == ConstraintSystemCompletionMode.FULL ->
+                    inferenceSession.customCompletionModeInsteadOfFull(call) ?: ConstraintSystemCompletionMode.FULL
+                else -> it
+            }
         }
 
         val analyzer = createPostponedArgumentsAnalyzer(transformer.resolutionContext)
@@ -101,35 +93,27 @@
 
         return when (completionMode) {
             ConstraintSystemCompletionMode.FULL -> {
-                if (inferenceSession.shouldRunCompletion(call)) {
-                    runCompletionForCall(candidate, completionMode, call, initialType, analyzer)
-                    val finalSubstitutor = candidate.system.asReadOnlyStorage()
-                        .buildAbstractResultingSubstitutor(session.typeContext) as ConeSubstitutor
-                    val completedCall = call.transformSingle(
-                        FirCallCompletionResultsWriterTransformer(
-                            session, components.scopeSession, finalSubstitutor,
-                            components.returnTypeCalculator,
-                            session.typeApproximator,
-                            components.dataFlowAnalyzer,
-                            components.integerLiteralAndOperatorApproximationTransformer,
-                            components.samResolver,
-                            components.context,
-                        ),
-                        null
-                    )
-                    inferenceSession.addCompletedCall(completedCall, candidate)
-                    completedCall
-                } else {
-                    inferenceSession.processPartiallyResolvedCall(call, resolutionMode)
-                    call
-                }
+                runCompletionForCall(candidate, completionMode, call, initialType, analyzer)
+                val finalSubstitutor = candidate.system.asReadOnlyStorage()
+                    .buildAbstractResultingSubstitutor(session.typeContext) as ConeSubstitutor
+                call.transformSingle(
+                    FirCallCompletionResultsWriterTransformer(
+                        session, components.scopeSession, finalSubstitutor,
+                        components.returnTypeCalculator,
+                        session.typeApproximator,
+                        components.dataFlowAnalyzer,
+                        components.integerLiteralAndOperatorApproximationTransformer,
+                        components.samResolver,
+                        components.context,
+                    ),
+                    null
+                )
             }
 
-            ConstraintSystemCompletionMode.PARTIAL -> {
+            ConstraintSystemCompletionMode.PARTIAL, ConstraintSystemCompletionMode.PCLA_POSTPONED_CALL -> {
                 runCompletionForCall(candidate, completionMode, call, initialType, analyzer)
-                if (inferenceSession is FirDelegatedPropertyInferenceSession) {
-                    inferenceSession.processPartiallyResolvedCall(call, resolutionMode)
-                }
+
+                inferenceSession.processPartiallyResolvedCall(call, resolutionMode, completionMode)
 
                 call
             }
@@ -183,8 +167,8 @@
         completionMode: ConstraintSystemCompletionMode,
         call: T,
         initialType: ConeKotlinType,
-        analyzer: PostponedArgumentsAnalyzer? = null
-    ) where T : FirResolvable, T : FirStatement {
+        analyzer: PostponedArgumentsAnalyzer? = null,
+    ) where T : FirStatement {
         @Suppress("NAME_SHADOWING")
         val analyzer = analyzer ?: createPostponedArgumentsAnalyzer(transformer.resolutionContext)
         completer.complete(
@@ -194,7 +178,7 @@
             initialType,
             transformer.resolutionContext
         ) {
-            analyzer.analyze(candidate.system, it, candidate, completionMode)
+            analyzer.analyze(candidate.system, it, candidate)
         }
     }
 
@@ -251,8 +235,9 @@
             contextReceivers: List<ConeKotlinType>,
             parameters: List<ConeKotlinType>,
             expectedReturnType: ConeKotlinType?,
-            stubsForPostponedVariables: Map<TypeVariableMarker, StubTypeMarker>,
-            candidate: Candidate
+            candidate: Candidate,
+            withPCLASession: Boolean,
+            forOverloadByLambdaReturnType: Boolean,
         ): ReturnArgumentsAnalysisResult {
             val lambdaArgument: FirAnonymousFunction = lambdaAtom.atom
             val needItParam = lambdaArgument.valueParameters.isEmpty() && parameters.size == 1
@@ -355,29 +340,36 @@
                 } ?: components.noExpectedType
             )
 
-            val builderInferenceSession = runIf(stubsForPostponedVariables.isNotEmpty()) {
-                @Suppress("UNCHECKED_CAST")
-                FirBuilderInferenceSession(
-                    lambdaArgument,
-                    transformer.resolutionContext,
-                    stubsForPostponedVariables as Map<ConeTypeVariable, ConeStubType>
-                )
-            }
+            var additionalConstraints: ConstraintStorage? = null
 
             transformer.context.withAnonymousFunctionTowerDataContext(lambdaArgument.symbol) {
-                if (builderInferenceSession != null) {
-                    transformer.context.withInferenceSession(builderInferenceSession) {
-                        lambdaArgument.transformSingle(transformer, ResolutionMode.LambdaResolution(expectedReturnTypeRef))
+                val pclaInferenceSession =
+                    runIf(withPCLASession) {
+                        candidate.lambdasAnalyzedWithPCLA += lambdaArgument
+
+                        FirPCLAInferenceSession(candidate, session.inferenceComponents, transformer.context.returnTypeCalculator)
+                    }
+
+                if (pclaInferenceSession != null) {
+                    transformer.context.withInferenceSession(pclaInferenceSession) {
+                        transformer.context.withOuterConstraintStorage(candidate.system.currentStorage()) {
+                            lambdaArgument.transformSingle(transformer, ResolutionMode.LambdaResolution(expectedReturnTypeRef))
+                        }
+
+                        applyResultsToMainCandidate()
                     }
                 } else {
-                    lambdaArgument.transformSingle(transformer, ResolutionMode.LambdaResolution(expectedReturnTypeRef))
+                    additionalConstraints =
+                        transformer.context.inferenceSession.runLambdaCompletion(candidate, forOverloadByLambdaReturnType) {
+                            lambdaArgument.transformSingle(transformer, ResolutionMode.LambdaResolution(expectedReturnTypeRef))
+                        }
                 }
             }
             transformer.context.dropContextForAnonymousFunction(lambdaArgument)
 
             val returnArguments = components.dataFlowAnalyzer.returnExpressionsOfAnonymousFunction(lambdaArgument).map { it.expression }
 
-            return ReturnArgumentsAnalysisResult(returnArguments, builderInferenceSession)
+            return ReturnArgumentsAnalysisResult(returnArguments, additionalConstraints)
         }
     }
 
diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/inference/FirDelegatedPropertyInferenceSession.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/inference/FirDelegatedPropertyInferenceSession.kt
index df3986d..2d75766 100644
--- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/inference/FirDelegatedPropertyInferenceSession.kt
+++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/inference/FirDelegatedPropertyInferenceSession.kt
@@ -5,52 +5,84 @@
 
 package org.jetbrains.kotlin.fir.resolve.inference
 
-import org.jetbrains.kotlin.fir.declarations.FirProperty
-import org.jetbrains.kotlin.fir.expressions.FirFunctionCall
-import org.jetbrains.kotlin.fir.expressions.FirFunctionCallOrigin
-import org.jetbrains.kotlin.fir.expressions.FirResolvable
-import org.jetbrains.kotlin.fir.expressions.FirStatement
+import org.jetbrains.kotlin.fir.FirElement
+import org.jetbrains.kotlin.fir.expressions.*
+import org.jetbrains.kotlin.fir.render
+import org.jetbrains.kotlin.fir.resolve.BodyResolveComponents
 import org.jetbrains.kotlin.fir.resolve.ResolutionMode
 import org.jetbrains.kotlin.fir.resolve.calls.Candidate
 import org.jetbrains.kotlin.fir.resolve.calls.InferenceError
 import org.jetbrains.kotlin.fir.resolve.calls.ResolutionContext
 import org.jetbrains.kotlin.fir.resolve.calls.candidate
+import org.jetbrains.kotlin.fir.resolve.initialTypeOfCandidate
 import org.jetbrains.kotlin.fir.resolve.substitution.ConeSubstitutor
+import org.jetbrains.kotlin.fir.resolve.transformers.FirCallCompletionResultsWriterTransformer
 import org.jetbrains.kotlin.fir.types.ConeClassLikeType
 import org.jetbrains.kotlin.fir.types.typeContext
+import org.jetbrains.kotlin.fir.visitors.transformSingle
 import org.jetbrains.kotlin.resolve.calls.inference.buildAbstractResultingSubstitutor
 import org.jetbrains.kotlin.resolve.calls.inference.components.ConstraintSystemCompletionMode
 import org.jetbrains.kotlin.resolve.calls.inference.model.ConstraintStorage
 import org.jetbrains.kotlin.util.OperatorNameConventions
+import org.jetbrains.kotlin.utils.addIfNotNull
 
 class FirDelegatedPropertyInferenceSession(
-    val property: FirProperty,
-    resolutionContext: ResolutionContext,
-    private val postponedArgumentsAnalyzer: PostponedArgumentsAnalyzer,
-) : FirInferenceSessionForChainedResolve(resolutionContext) {
+    private val resolutionContext: ResolutionContext,
+    private val callCompleter: FirCallCompleter,
+    private val delegateExpression: FirExpression,
+) : FirInferenceSession() {
 
-    private var currentConstraintSystem = components.session.inferenceComponents.createConstraintSystem()
+    private val partiallyResolvedCalls: MutableList<Pair<FirResolvable, Candidate>> = mutableListOf()
+
+    private val components: BodyResolveComponents
+        get() = resolutionContext.bodyResolveComponents
+
+    private val FirResolvable.candidate: Candidate
+        get() = candidate()!!
+
+    private val nonTrivialParentSession: FirInferenceSession? =
+        resolutionContext.bodyResolveContext.inferenceSession.takeIf { it !== DEFAULT }
+
+    private val delegateCandidate = (delegateExpression as? FirResolvable)?.candidate()
+    private val parentConstraintSystem =
+        delegateCandidate?.system
+            ?: (resolutionContext.bodyResolveContext.inferenceSession as? FirPCLAInferenceSession)?.currentCommonSystem
+            ?: components.session.inferenceComponents.createConstraintSystem()
+
+    private val currentConstraintSystem =
+        prepareSharedBaseSystem(parentConstraintSystem, components.session.inferenceComponents)
+
     val currentConstraintStorage: ConstraintStorage get() = currentConstraintSystem.currentStorage()
 
     private val unitType: ConeClassLikeType = components.session.builtinTypes.unitType.type
 
+    private var wasCompletionRun = false
+
     // TODO after PCLA (KT-59107):
     //  Outer system seems to be a property of a concrete call resolution and probably should be applied to concrete call resolution
     override fun <R> onCandidatesResolution(call: FirFunctionCall, candidatesResolutionCallback: () -> R): R {
-        return if (!call.isAnyOfDelegateOperators())
-            candidatesResolutionCallback()
-        else
-            resolutionContext.bodyResolveContext.withOuterConstraintStorage(
-                currentConstraintSystem.currentStorage(),
-                candidatesResolutionCallback
-            )
+        if (wasCompletionRun || !call.isAnyOfDelegateOperators()) return candidatesResolutionCallback()
+        requireCallIsDelegateOperator(call)
+
+        return resolutionContext.bodyResolveContext.withOuterConstraintStorage(
+            currentConstraintSystem.currentStorage(),
+            candidatesResolutionCallback
+        )
     }
 
-    override fun <T> shouldAvoidFullCompletion(call: T): Boolean where T : FirResolvable, T : FirStatement =
-        call.isAnyOfDelegateOperators()
+    override fun customCompletionModeInsteadOfFull(call: FirResolvable): ConstraintSystemCompletionMode? = when {
+        call.isAnyOfDelegateOperators() && !wasCompletionRun -> ConstraintSystemCompletionMode.PARTIAL
+        else -> null
+    }
 
-    override fun <T> processPartiallyResolvedCall(call: T, resolutionMode: ResolutionMode) where T : FirResolvable, T : FirStatement {
-        if (resolutionMode != ResolutionMode.ContextDependent.Delegate && !call.isAnyOfDelegateOperators()) return
+    override fun <T> processPartiallyResolvedCall(
+        call: T,
+        resolutionMode: ResolutionMode,
+        completionMode: ConstraintSystemCompletionMode,
+    ) where T : FirResolvable, T : FirStatement {
+        if (wasCompletionRun || !call.isAnyOfDelegateOperators()) return
+
+        requireCallIsDelegateOperator(call)
 
         val candidate = call.candidate
 
@@ -61,31 +93,82 @@
         val candidateSystem = candidate.system
 
         partiallyResolvedCalls.add(call to candidate)
-        currentConstraintSystem = candidateSystem
+        currentConstraintSystem.addOtherSystem(candidateSystem.currentStorage())
+    }
+
+    private fun <T> requireCallIsDelegateOperator(call: T) where T : FirResolvable, T : FirStatement {
+        require(call.isAnyOfDelegateOperators()) {
+            "Unexpected ${call.render()} call"
+        }
     }
 
     private fun <T> T.isProvideDelegate() where T : FirResolvable, T : FirStatement =
-        isAnyOfDelegateOperators() && (this as FirFunctionCall).calleeReference.name == OperatorNameConventions.PROVIDE_DELEGATE
+        isAnyOfDelegateOperators() && (this as FirResolvable).candidate()?.callInfo?.name == OperatorNameConventions.PROVIDE_DELEGATE
 
-    private fun <T> T.isAnyOfDelegateOperators(): Boolean where T : FirResolvable {
-        if (this !is FirFunctionCall || origin != FirFunctionCallOrigin.Operator) return false
-        val name = calleeReference.name
-        return name == OperatorNameConventions.PROVIDE_DELEGATE || name == OperatorNameConventions.GET_VALUE || name == OperatorNameConventions.SET_VALUE
+    override fun baseConstraintStorageForCandidate(candidate: Candidate): ConstraintStorage? =
+        resolutionContext.bodyResolveContext.outerConstraintStorage.takeIf { it !== ConstraintStorage.Empty }
+
+    fun completeSessionOrPostponeIfNonRoot(onCompletionResultsWriting: (ConeSubstitutor) -> Unit) {
+        check(!wasCompletionRun)
+        wasCompletionRun = true
+
+        parentConstraintSystem.addOtherSystem(currentConstraintStorage)
+
+        (nonTrivialParentSession as? FirPCLAInferenceSession)?.apply {
+            if (delegateCandidate != null) {
+                callCompleter.runCompletionForCall(
+                    delegateCandidate,
+                    ConstraintSystemCompletionMode.PCLA_POSTPONED_CALL,
+                    delegateExpression,
+                    components.initialTypeOfCandidate(delegateCandidate)
+                )
+            }
+
+            integrateChildSession(
+                buildList {
+                    addIfNotNull(delegateExpression)
+                    partiallyResolvedCalls.mapTo(this) { it.first as FirStatement }
+                },
+                parentConstraintSystem.currentStorage(),
+                onCompletionResultsWriting,
+            )
+            return
+        }
+
+        val completedCalls = completeCandidatesForRootSession()
+
+        val finalSubstitutor = parentConstraintSystem.asReadOnlyStorage()
+            .buildAbstractResultingSubstitutor(components.session.typeContext) as ConeSubstitutor
+
+        val callCompletionResultsWriter = callCompleter.createCompletionResultsWriter(
+            finalSubstitutor,
+            // TODO: Get rid of the mode
+            mode = FirCallCompletionResultsWriterTransformer.Mode.DelegatedPropertyCompletion
+        )
+        completedCalls.forEach {
+            it.transformSingle(callCompletionResultsWriter, null)
+        }
+
+        onCompletionResultsWriting(finalSubstitutor)
     }
 
-    fun completeCandidates(): List<FirResolvable> {
-        val commonSystem = currentConstraintSystem.apply { prepareForGlobalCompletion() }
+    private fun completeCandidatesForRootSession(): List<FirResolvable> {
+        val parentSystem = parentConstraintSystem.apply { prepareForGlobalCompletion() }
 
-        val notCompletedCalls = partiallyResolvedCalls.mapNotNull { partiallyResolvedCall ->
-            partiallyResolvedCall.first.takeIf { resolvable ->
-                resolvable.candidate() != null
+        val notCompletedCalls =
+            buildList {
+                addIfNotNull(delegateExpression as? FirResolvable)
+                partiallyResolvedCalls.mapNotNullTo(this) { partiallyResolvedCall ->
+                    partiallyResolvedCall.first.takeIf { resolvable ->
+                        resolvable.candidate() != null
+                    }
+                }
             }
-        }
 
         resolutionContext.bodyResolveContext.withInferenceSession(DEFAULT) {
             @Suppress("UNCHECKED_CAST")
             components.callCompleter.completer.complete(
-                commonSystem.asConstraintSystemCompleterContext(),
+                parentSystem.asConstraintSystemCompleterContext(),
                 ConstraintSystemCompletionMode.FULL,
                 notCompletedCalls as List<FirStatement>,
                 unitType, resolutionContext
@@ -100,29 +183,31 @@
                     }
                     found
                 }.candidate
-                postponedArgumentsAnalyzer.analyze(
-                    commonSystem,
+                callCompleter.createPostponedArgumentsAnalyzer(resolutionContext).analyze(
+                    parentSystem,
                     lambdaAtom,
                     containingCandidateForLambda,
-                    ConstraintSystemCompletionMode.FULL,
                 )
             }
         }
 
-        for ((_, candidate) in partiallyResolvedCalls) {
-            for (error in commonSystem.errors) {
+        for (candidate in notCompletedCalls.mapNotNull { it.candidate() }) {
+            for (error in parentSystem.errors) {
                 candidate.addDiagnostic(InferenceError(error))
             }
         }
 
         return notCompletedCalls
     }
+}
 
-    fun createFinalSubstitutor(): ConeSubstitutor =
-        currentConstraintSystem.asReadOnlyStorage()
-            .buildAbstractResultingSubstitutor(components.session.typeContext) as ConeSubstitutor
+fun FirElement.isAnyOfDelegateOperators(): Boolean {
+    if (this is FirPropertyAccessExpression) {
+        val originalCall = this.candidate()?.callInfo?.callSite as? FirFunctionCall ?: return false
+        return originalCall.isAnyOfDelegateOperators()
+    }
 
-    override fun <T> addCompletedCall(call: T, candidate: Candidate) where T : FirResolvable, T : FirStatement {}
-
-    override fun <T> shouldRunCompletion(call: T): Boolean where T : FirResolvable, T : FirStatement = true
+    if (this !is FirFunctionCall || origin != FirFunctionCallOrigin.Operator) return false
+    val name = calleeReference.name
+    return name == OperatorNameConventions.PROVIDE_DELEGATE || name == OperatorNameConventions.GET_VALUE || name == OperatorNameConventions.SET_VALUE
 }
diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/inference/FirInferenceSession.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/inference/FirInferenceSession.kt
index 73b4119..fb32c71 100644
--- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/inference/FirInferenceSession.kt
+++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/inference/FirInferenceSession.kt
@@ -5,52 +5,60 @@
 
 package org.jetbrains.kotlin.fir.resolve.inference
 
+import org.jetbrains.kotlin.fir.FirElement
 import org.jetbrains.kotlin.fir.expressions.FirFunctionCall
 import org.jetbrains.kotlin.fir.expressions.FirResolvable
 import org.jetbrains.kotlin.fir.expressions.FirStatement
 import org.jetbrains.kotlin.fir.resolve.ResolutionMode
 import org.jetbrains.kotlin.fir.resolve.calls.Candidate
 import org.jetbrains.kotlin.fir.types.ConeKotlinType
-import org.jetbrains.kotlin.fir.types.ConeTypeVariableTypeConstructor
-import org.jetbrains.kotlin.resolve.calls.inference.ConstraintSystemBuilder
 import org.jetbrains.kotlin.resolve.calls.inference.components.ConstraintSystemCompletionMode
+import org.jetbrains.kotlin.resolve.calls.inference.model.ConstraintStorage
+import org.jetbrains.kotlin.resolve.calls.inference.model.NewConstraintSystemImpl
 
 abstract class FirInferenceSession {
     companion object {
-        val DEFAULT: FirInferenceSession = object : FirStubInferenceSession() {}
+        val DEFAULT: FirInferenceSession = object : FirInferenceSession() {
+            override fun <T> processPartiallyResolvedCall(
+                call: T,
+                resolutionMode: ResolutionMode,
+                completionMode: ConstraintSystemCompletionMode,
+            ) where T : FirResolvable, T : FirStatement {
+                // Do nothing
+            }
+        }
+
+        @JvmStatic
+        protected fun prepareSharedBaseSystem(
+            outerSystem: NewConstraintSystemImpl,
+            components: InferenceComponents,
+        ): NewConstraintSystemImpl {
+            return components.createConstraintSystem().apply {
+                addOuterSystem(outerSystem.currentStorage())
+            }
+        }
     }
 
-    abstract fun <T> shouldRunCompletion(call: T): Boolean where T : FirResolvable, T : FirStatement
+    open fun runLambdaCompletion(candidate: Candidate, forOverloadByLambdaReturnType: Boolean, block: () -> Unit): ConstraintStorage? {
+        block()
+        return null
+    }
 
-    /**
-     * In some cases (like getValue/setValue resolution of property delegation convention), it might be necessary to postpone full completion
-     * even with the ContextIndependent mode (which is used for delegated accessors bodies)
-     */
-    open fun <T> shouldAvoidFullCompletion(call: T): Boolean where T : FirResolvable, T : FirStatement = false
+    open fun <T> runCallableReferenceResolution(candidate: Candidate, block: () -> T): T = block()
 
-    abstract fun <T> processPartiallyResolvedCall(call: T, resolutionMode: ResolutionMode) where T : FirResolvable, T : FirStatement
-    abstract fun <T> addCompletedCall(call: T, candidate: Candidate) where T : FirResolvable, T : FirStatement
+    open fun customCompletionModeInsteadOfFull(
+        call: FirResolvable,
+    ): ConstraintSystemCompletionMode? = null
 
-    abstract fun inferPostponedVariables(
-        lambda: ResolvedLambdaAtom,
-        constraintSystemBuilder: ConstraintSystemBuilder,
-        completionMode: ConstraintSystemCompletionMode,
-        candidate: Candidate
-    ): Map<ConeTypeVariableTypeConstructor, ConeKotlinType>?
+    abstract fun <T> processPartiallyResolvedCall(
+        call: T,
+        resolutionMode: ResolutionMode,
+        completionMode: ConstraintSystemCompletionMode
+    ) where T : FirResolvable, T : FirStatement
 
     open fun <R> onCandidatesResolution(call: FirFunctionCall, candidatesResolutionCallback: () -> R) = candidatesResolutionCallback()
-}
 
-abstract class FirStubInferenceSession : FirInferenceSession() {
-    override fun <T> shouldRunCompletion(call: T): Boolean where T : FirResolvable, T : FirStatement = true
+    open fun baseConstraintStorageForCandidate(candidate: Candidate): ConstraintStorage? = null
 
-    override fun <T> processPartiallyResolvedCall(call: T, resolutionMode: ResolutionMode) where T : FirResolvable, T : FirStatement {}
-    override fun <T> addCompletedCall(call: T, candidate: Candidate) where T : FirResolvable, T : FirStatement {}
-
-    override fun inferPostponedVariables(
-        lambda: ResolvedLambdaAtom,
-        constraintSystemBuilder: ConstraintSystemBuilder,
-        completionMode: ConstraintSystemCompletionMode,
-        candidate: Candidate
-    ): Map<ConeTypeVariableTypeConstructor, ConeKotlinType>? = null
+    open fun addSubtypeConstraintIfCompatible(lowerType: ConeKotlinType, upperType: ConeKotlinType, element: FirElement) {}
 }
diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/inference/FirInferenceSessionForChainedResolve.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/inference/FirInferenceSessionForChainedResolve.kt
deleted file mode 100644
index eceadc7..0000000
--- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/inference/FirInferenceSessionForChainedResolve.kt
+++ /dev/null
@@ -1,43 +0,0 @@
-/*
- * Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
- * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
- */
-
-package org.jetbrains.kotlin.fir.resolve.inference
-
-import org.jetbrains.kotlin.fir.expressions.FirResolvable
-import org.jetbrains.kotlin.fir.expressions.FirStatement
-import org.jetbrains.kotlin.fir.resolve.BodyResolveComponents
-import org.jetbrains.kotlin.fir.resolve.ResolutionMode
-import org.jetbrains.kotlin.fir.resolve.calls.Candidate
-import org.jetbrains.kotlin.fir.resolve.calls.ResolutionContext
-import org.jetbrains.kotlin.fir.resolve.calls.candidate
-import org.jetbrains.kotlin.types.model.KotlinTypeMarker
-import org.jetbrains.kotlin.types.model.TypeConstructorMarker
-import org.jetbrains.kotlin.types.model.TypeSubstitutorMarker
-import org.jetbrains.kotlin.types.model.safeSubstitute
-
-abstract class FirInferenceSessionForChainedResolve(
-    protected val resolutionContext: ResolutionContext
-) : FirInferenceSession() {
-    protected val partiallyResolvedCalls: MutableList<Pair<FirResolvable, Candidate>> = mutableListOf()
-
-    protected val components: BodyResolveComponents
-        get() = resolutionContext.bodyResolveComponents
-
-    override fun <T> addCompletedCall(call: T, candidate: Candidate) where T : FirResolvable, T : FirStatement {
-        // do nothing
-    }
-
-    override fun <T> processPartiallyResolvedCall(call: T, resolutionMode: ResolutionMode) where T : FirResolvable, T : FirStatement {
-        partiallyResolvedCalls += call to call.candidate
-    }
-
-    protected val FirResolvable.candidate: Candidate
-        get() = candidate()!!
-
-    override fun clear() {
-        partiallyResolvedCalls.clear()
-        completedCalls.clear()
-    }
-}
diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/inference/FirPCLAInferenceSession.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/inference/FirPCLAInferenceSession.kt
new file mode 100644
index 0000000..cb4ebbc
--- /dev/null
+++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/inference/FirPCLAInferenceSession.kt
@@ -0,0 +1,305 @@
+/*
+ * Copyright 2010-2023 JetBrains s.r.o. and Kotlin Programming Language contributors.
+ * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
+ */
+
+package org.jetbrains.kotlin.fir.resolve.inference
+
+import org.jetbrains.kotlin.fir.FirElement
+import org.jetbrains.kotlin.fir.expressions.*
+import org.jetbrains.kotlin.fir.resolve.ResolutionMode
+import org.jetbrains.kotlin.fir.resolve.calls.CallKind
+import org.jetbrains.kotlin.fir.resolve.calls.Candidate
+import org.jetbrains.kotlin.fir.resolve.calls.candidate
+import org.jetbrains.kotlin.fir.resolve.inference.model.ConeExpectedTypeConstraintPosition
+import org.jetbrains.kotlin.fir.resolve.inference.model.ConeFixVariableConstraintPosition
+import org.jetbrains.kotlin.fir.resolve.substitution.ConeSubstitutor
+import org.jetbrains.kotlin.fir.resolve.transformers.ReturnTypeCalculator
+import org.jetbrains.kotlin.fir.symbols.impl.FirVariableSymbol
+import org.jetbrains.kotlin.fir.types.*
+import org.jetbrains.kotlin.fir.visitors.FirDefaultTransformer
+import org.jetbrains.kotlin.resolve.calls.inference.addSubtypeConstraintIfCompatible
+import org.jetbrains.kotlin.resolve.calls.inference.components.ConstraintSystemCompletionMode
+import org.jetbrains.kotlin.resolve.calls.inference.components.TypeVariableDirectionCalculator
+import org.jetbrains.kotlin.resolve.calls.inference.model.ConstraintStorage
+import org.jetbrains.kotlin.resolve.calls.inference.model.NewConstraintSystemImpl
+import org.jetbrains.kotlin.types.model.TypeConstructorMarker
+import org.jetbrains.kotlin.types.model.defaultType
+
+
+class FirPCLAInferenceSession(
+    private val outerCandidate: Candidate,
+    private val inferenceComponents: InferenceComponents,
+    private val returnTypeCalculator: ReturnTypeCalculator,
+) : FirInferenceSession() {
+
+    var currentCommonSystem = prepareSharedBaseSystem(outerCandidate.system, inferenceComponents)
+        private set
+
+    override fun customCompletionModeInsteadOfFull(
+        call: FirResolvable,
+    ): ConstraintSystemCompletionMode? = when {
+        call.candidate()?.usedOuterCs == true -> ConstraintSystemCompletionMode.PCLA_POSTPONED_CALL
+        else -> null
+    }
+
+    override fun runLambdaCompletion(candidate: Candidate, forOverloadByLambdaReturnType: Boolean, block: () -> Unit): ConstraintStorage? {
+        if (forOverloadByLambdaReturnType) {
+            val constraintAccumulatorForLambda =
+                inferenceComponents.createConstraintSystem().apply {
+                    setBaseSystem(currentCommonSystem.currentStorage())
+                }
+
+            runWithSpecifiedCurrentCommonSystem(constraintAccumulatorForLambda, block)
+
+            return constraintAccumulatorForLambda.currentStorage()
+        }
+
+        runWithSpecifiedCurrentCommonSystem(candidate.system, block)
+
+        return null
+    }
+
+    override fun <T> runCallableReferenceResolution(candidate: Candidate, block: () -> T): T {
+        candidate.system.apply {
+            // It's necessary because otherwise when we create CS for a child, it would simplify constraints
+            // (see 3rd constructor of MutableVariableWithConstraints)
+            // and merging it back might become a problem for transaction logic because the latter literally remembers
+            // the number of constraints for each variable and then restores it back.
+            // But since the constraints are simplified in the child, their number might become even fewer, leading to incorrect behavior
+            // or runtime exceptions.
+            // See callableReferenceAsArgumentForTransaction.kt test data
+            notFixedTypeVariables.values.forEach { it.runConstraintsSimplification() }
+        }
+        return runWithSpecifiedCurrentCommonSystem(candidate.system, block)
+    }
+
+    private fun <T> runWithSpecifiedCurrentCommonSystem(newSystem: NewConstraintSystemImpl, block: () -> T): T {
+        val previous = currentCommonSystem
+        return try {
+            currentCommonSystem = newSystem
+            block()
+        } finally {
+            currentCommonSystem = previous
+        }
+    }
+
+    override fun <T> processPartiallyResolvedCall(
+        call: T,
+        resolutionMode: ResolutionMode,
+        completionMode: ConstraintSystemCompletionMode,
+    ) where T : FirResolvable, T : FirStatement {
+        if (call is FirExpression) {
+            call.updateReturnTypeWithCurrentSubstitutor(resolutionMode)
+        }
+
+        val candidate = call.candidate() ?: return
+        if (!candidate.usedOuterCs) return
+
+        // Integrating back would happen at FirDelegatedPropertyInferenceSession.completeSessionOrPostponeIfNonRoot
+        // after all other delegation-related calls are being analyzed
+        if (resolutionMode == ResolutionMode.ContextDependent.Delegate) return
+
+        currentCommonSystem.replaceContentWith(candidate.system.currentStorage())
+
+        if (completionMode == ConstraintSystemCompletionMode.PCLA_POSTPONED_CALL) {
+            outerCandidate.postponedPCLACalls += call
+        }
+    }
+
+    fun applyResultsToMainCandidate() {
+        outerCandidate.system.replaceContentWith(currentCommonSystem.currentStorage())
+    }
+
+    fun integrateChildSession(
+        childCalls: Collection<FirStatement>,
+        childStorage: ConstraintStorage,
+        onCompletionResultsWriting: (ConeSubstitutor) -> Unit,
+    ) {
+        outerCandidate.postponedPCLACalls += childCalls
+        currentCommonSystem.addOtherSystem(childStorage)
+        outerCandidate.onCompletionResultsWritingCallbacks += onCompletionResultsWriting
+    }
+
+    private fun FirExpression.updateReturnTypeWithCurrentSubstitutor(
+        resolutionMode: ResolutionMode,
+    ) {
+        val additionalBindings = mutableMapOf<TypeConstructorMarker, ConeKotlinType>()
+        val system = (this as? FirResolvable)?.candidate()?.system ?: currentCommonSystem
+
+        resolutionMode.hashCode()
+        if (resolutionMode is ResolutionMode.ReceiverResolution) {
+            fixVariablesForMemberScope(resolvedType, system)?.let { additionalBindings += it }
+        }
+
+        val substitutor = system.buildCurrentSubstitutor(additionalBindings) as ConeSubstitutor
+        val updatedType = substitutor.substituteOrNull(resolvedType)
+
+        if (updatedType != null) {
+            replaceConeTypeOrNull(updatedType)
+        }
+    }
+
+    fun fixVariablesForMemberScope(
+        type: ConeKotlinType,
+        myCs: NewConstraintSystemImpl,
+    ): Pair<ConeTypeVariableTypeConstructor, ConeKotlinType>? {
+        return when (type) {
+            is ConeFlexibleType -> fixVariablesForMemberScope(type.lowerBound, myCs)
+            is ConeDefinitelyNotNullType -> fixVariablesForMemberScope(type.original, myCs)
+            is ConeTypeVariableType -> fixVariablesForMemberScope(type, myCs)
+            else -> null
+        }
+    }
+
+    private fun fixVariablesForMemberScope(
+        type: ConeTypeVariableType,
+        myCs: NewConstraintSystemImpl,
+    ): Pair<ConeTypeVariableTypeConstructor, ConeKotlinType>? {
+        val coneTypeVariableTypeConstructor = type.typeConstructor
+
+        require(coneTypeVariableTypeConstructor in myCs.allTypeVariables) {
+            "$coneTypeVariableTypeConstructor not found"
+        }
+
+        if (coneTypeVariableTypeConstructor in myCs.outerTypeVariables.orEmpty()) return null
+
+        val variableWithConstraints = myCs.notFixedTypeVariables[coneTypeVariableTypeConstructor] ?: return null
+        val c = myCs.getBuilder()
+
+        val resultType = c.run {
+            c.withTypeVariablesThatAreCountedAsProperTypes(c.outerTypeVariables.orEmpty()) {
+                if (!inferenceComponents.variableFixationFinder.isTypeVariableHasProperConstraint(c, coneTypeVariableTypeConstructor)) {
+                    return@withTypeVariablesThatAreCountedAsProperTypes null
+                }
+                inferenceComponents.resultTypeResolver.findResultType(
+                    c,
+                    variableWithConstraints,
+                    TypeVariableDirectionCalculator.ResolveDirection.UNKNOWN
+                ) as ConeKotlinType
+            }
+        } ?: return null
+        val variable = variableWithConstraints.typeVariable
+        // TODO: Position
+        c.addEqualityConstraint(variable.defaultType(c), resultType, ConeFixVariableConstraintPosition(variable))
+
+        return Pair(coneTypeVariableTypeConstructor, resultType)
+    }
+
+    override fun baseConstraintStorageForCandidate(candidate: Candidate): ConstraintStorage? {
+        if (candidate.needsToBePostponed()) return currentCommonSystem.currentStorage()
+        return null
+    }
+
+    private fun FirExpression.isLambda(): Boolean {
+        if (this is FirWrappedArgumentExpression) return expression.isLambda()
+        if (this is FirAnonymousFunctionExpression) return true
+        if (this is FirCallableReferenceAccess) return true
+        if (this is FirAnonymousObjectExpression) return true
+        return false
+    }
+
+    private fun Candidate.needsToBePostponed(): Boolean {
+        if (dispatchReceiver?.isReceiverPostponed() == true) return true
+        if (givenExtensionReceiverOptions.any { it.isReceiverPostponed() }) return true
+
+        if (callInfo.arguments.any { it.isLambda() }) return true
+
+        if (callInfo.callKind == CallKind.VariableAccess) {
+            val returnType = (symbol as? FirVariableSymbol)?.let(returnTypeCalculator::tryCalculateReturnType)
+            if (returnType?.type?.containsNotFixedTypeVariables() == true) return true
+        }
+        if (callInfo.arguments.any { it.isQualifiedAccessContainingTypeVariables() }) return true
+
+        if (callInfo.resolutionMode is ResolutionMode.ContextDependent.Delegate) return true
+
+        // For assignments like myVarContainingTV = SomeCallWithNonTrivialInference(...)
+        // We should integrate the call into the PCLA tree, too
+        if ((callInfo.resolutionMode as? ResolutionMode.WithExpectedType)?.expectedTypeRef?.type?.containsNotFixedTypeVariables() == true) {
+            return true
+        }
+
+        // Synthetic calls with blocks work like lambdas
+        if (callInfo.callKind == CallKind.SyntheticSelect) return true
+
+        return false
+    }
+
+    private fun FirExpression.isReceiverPostponed(): Boolean {
+        if (resolvedType.containsNotFixedTypeVariables()) return true
+        if ((this as? FirResolvable)?.candidate()?.usedOuterCs == true) return true
+
+        return false
+    }
+
+    private fun FirExpression.isQualifiedAccessContainingTypeVariables(): Boolean {
+        if (this is FirNamedArgumentExpression) return expression.isQualifiedAccessContainingTypeVariables()
+
+        if (!isQualifiedAccessOrSmartCastOnIt()) return false
+        if (this is FirCallableReferenceAccess) return false
+
+        return resolvedType.containsNotFixedTypeVariables() && (this as? FirResolvable)?.candidate() == null
+    }
+
+    private fun FirExpression.isQualifiedAccessOrSmartCastOnIt(): Boolean = when (this) {
+        is FirQualifiedAccessExpression -> true
+        is FirSmartCastExpression -> originalExpression.isQualifiedAccessOrSmartCastOnIt()
+        else -> false
+    }
+
+    private fun ConeKotlinType.containsNotFixedTypeVariables(): Boolean =
+        contains {
+            it is ConeTypeVariableType && it.typeConstructor in currentCommonSystem.allTypeVariables
+        }
+
+    override fun addSubtypeConstraintIfCompatible(lowerType: ConeKotlinType, upperType: ConeKotlinType, element: FirElement) {
+        currentCommonSystem.addSubtypeConstraintIfCompatible(lowerType, upperType, ConeExpectedTypeConstraintPosition)
+    }
+}
+
+class FirStubTypeTransformer(private val substitutor: ConeSubstitutor) : FirDefaultTransformer<Nothing?>() {
+
+    override fun <E : FirElement> transformElement(element: E, data: Nothing?): E {
+        // All resolvable nodes should be implemented separately to cover substitution of receivers in the candidate
+        if (element is FirResolvable) {
+            element.candidate()?.let { processCandidate(it) }
+        }
+
+        // Since FirExpressions don't have typeRefs, they need to be updated separately.
+        // FirAnonymousFunctionExpression doesn't support replacing the type
+        // since it delegates the getter to the underlying FirAnonymousFunction.
+        if (element is FirExpression && element !is FirAnonymousFunctionExpression) {
+            // TODO Check why some expressions have unresolved type in builder inference session KT-61835
+            @OptIn(UnresolvedExpressionTypeAccess::class)
+            element.coneTypeOrNull
+                ?.let(substitutor::substituteOrNull)
+                ?.let { element.replaceConeTypeOrNull(it) }
+        }
+
+        @Suppress("UNCHECKED_CAST")
+        return element.transformChildren(this, data = null) as E
+    }
+
+    override fun transformTypeOperatorCall(typeOperatorCall: FirTypeOperatorCall, data: Nothing?): FirStatement {
+        if (typeOperatorCall.argument.resolvedType is ConeStubType) {
+            typeOperatorCall.replaceArgFromStubType(true)
+        }
+        return super.transformTypeOperatorCall(typeOperatorCall, data)
+    }
+
+    override fun transformResolvedTypeRef(resolvedTypeRef: FirResolvedTypeRef, data: Nothing?): FirTypeRef =
+        substitutor.substituteOrNull(resolvedTypeRef.type)?.let {
+            resolvedTypeRef.withReplacedConeType(it)
+        } ?: resolvedTypeRef
+
+    /*
+     * We should manually update all receivers in the all not completed candidates, because not all calls with candidates
+     *   contained in partiallyResolvedCalls and candidate stores not receiver values, which are updated, (TODO: remove this comment after removal of updating values)
+     *   and receivers of candidates are not direct FIR children of calls, so they won't be visited during regular transformChildren
+     */
+    private fun processCandidate(candidate: Candidate) {
+        candidate.dispatchReceiver = candidate.dispatchReceiver?.transform(this, data = null)
+        candidate.chosenExtensionReceiver = candidate.chosenExtensionReceiver?.transform(this, data = null)
+        candidate.contextReceiverArguments = candidate.contextReceiverArguments?.map { it.transform(this, data = null) }
+    }
+}
diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/inference/PostponedArgumentsAnalyzer.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/inference/PostponedArgumentsAnalyzer.kt
index 2012422..902396b 100644
--- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/inference/PostponedArgumentsAnalyzer.kt
+++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/inference/PostponedArgumentsAnalyzer.kt
@@ -19,13 +19,16 @@
 import org.jetbrains.kotlin.fir.types.*
 import org.jetbrains.kotlin.resolve.calls.components.PostponedArgumentsAnalyzerContext
 import org.jetbrains.kotlin.resolve.calls.inference.ConstraintSystemBuilder
-import org.jetbrains.kotlin.resolve.calls.inference.components.ConstraintSystemCompletionMode
-import org.jetbrains.kotlin.resolve.calls.inference.model.BuilderInferencePosition
-import org.jetbrains.kotlin.types.model.*
+import org.jetbrains.kotlin.resolve.calls.inference.addSubtypeConstraintIfCompatible
+import org.jetbrains.kotlin.resolve.calls.inference.model.ConstraintStorage
+import org.jetbrains.kotlin.types.model.KotlinTypeMarker
+import org.jetbrains.kotlin.types.model.TypeConstructorMarker
+import org.jetbrains.kotlin.types.model.freshTypeConstructor
+import org.jetbrains.kotlin.types.model.safeSubstitute
 
 data class ReturnArgumentsAnalysisResult(
     val returnArguments: Collection<FirExpression>,
-    val inferenceSession: FirInferenceSession?
+    val additionalConstraints: ConstraintStorage?,
 )
 
 interface LambdaAnalyzer {
@@ -35,8 +38,9 @@
         contextReceivers: List<ConeKotlinType>,
         parameters: List<ConeKotlinType>,
         expectedReturnType: ConeKotlinType?, // null means, that return type is not proper i.e. it depends on some type variables
-        stubsForPostponedVariables: Map<TypeVariableMarker, StubTypeMarker>,
-        candidate: Candidate
+        candidate: Candidate,
+        withPCLASession: Boolean,
+        forOverloadByLambdaReturnType: Boolean,
     ): ReturnArgumentsAnalysisResult
 }
 
@@ -44,21 +48,24 @@
     private val resolutionContext: ResolutionContext,
     private val lambdaAnalyzer: LambdaAnalyzer,
     private val components: InferenceComponents,
-    private val callResolver: FirCallResolver
+    private val callResolver: FirCallResolver,
 ) {
 
     fun analyze(
         c: PostponedArgumentsAnalyzerContext,
         argument: PostponedResolvedAtom,
         candidate: Candidate,
-        completionMode: ConstraintSystemCompletionMode
     ) {
         when (argument) {
             is ResolvedLambdaAtom ->
-                analyzeLambda(c, argument, candidate, completionMode)
+                analyzeLambda(c, argument, candidate, forOverloadByLambdaReturnType = false)
 
             is LambdaWithTypeVariableAsExpectedTypeAtom ->
-                analyzeLambda(c, argument.transformToResolvedLambda(c.getBuilder(), resolutionContext), candidate, completionMode)
+                analyzeLambda(
+                    c,
+                    argument.transformToResolvedLambda(c.getBuilder(), resolutionContext),
+                    candidate, forOverloadByLambdaReturnType = false
+                )
 
             is ResolvedCallableReferenceAtom -> processCallableReference(argument, candidate)
         }
@@ -66,7 +73,7 @@
 
     private fun processCallableReference(atom: ResolvedCallableReferenceAtom, candidate: Candidate) {
         if (atom.mightNeedAdditionalResolution) {
-            callResolver.resolveCallableReference(candidate.csBuilder, atom, hasSyntheticOuterCall = false)
+            callResolver.resolveCallableReference(candidate, atom, hasSyntheticOuterCall = false)
         }
 
         val callableReferenceAccess = atom.reference
@@ -98,17 +105,30 @@
         c: PostponedArgumentsAnalyzerContext,
         lambda: ResolvedLambdaAtom,
         candidate: Candidate,
-        completionMode: ConstraintSystemCompletionMode,
+        forOverloadByLambdaReturnType: Boolean,
         //diagnosticHolder: KotlinDiagnosticsHolder
     ): ReturnArgumentsAnalysisResult {
         // TODO: replace with `require(!lambda.analyzed)` when KT-54767 will be fixed
         if (lambda.analyzed) {
-            return ReturnArgumentsAnalysisResult(lambda.returnStatements, inferenceSession = null)
+            return ReturnArgumentsAnalysisResult(lambda.returnStatements, additionalConstraints = null)
         }
 
+        val additionalBindings: Map<TypeConstructorMarker, KotlinTypeMarker>? =
+            (resolutionContext.bodyResolveContext.inferenceSession as? FirPCLAInferenceSession)?.let { builderInferenceSession ->
+                // TODO: context receivers
+                buildMap {
+                    lambda.receiver
+                        ?.let { builderInferenceSession.fixVariablesForMemberScope(it, candidate.system) }
+                        ?.let(this::plusAssign)
+
+                    for (parameterType in lambda.parameters) {
+                        builderInferenceSession.fixVariablesForMemberScope(parameterType, candidate.system)?.let(this::plusAssign)
+                    }
+                }
+            }
+
         val unitType = components.session.builtinTypes.unitType.type
-        val stubsForPostponedVariables = c.bindingStubsForPostponedVariables()
-        val currentSubstitutor = c.buildCurrentSubstitutor(stubsForPostponedVariables.mapKeys { it.key.freshTypeConstructor(c) })
+        val currentSubstitutor = c.buildCurrentSubstitutor(additionalBindings ?: emptyMap()) as ConeSubstitutor
 
         fun substitute(type: ConeKotlinType) = currentSubstitutor.safeSubstitute(c, type) as ConeKotlinType
 
@@ -126,21 +146,27 @@
             else -> null
         }
 
+        val withPCLASession =
+            lambda.inputTypes
+                .any { inputType ->
+                    with(c) { inputType.extractTypeVariables() }.any(c.notFixedTypeVariables::contains)
+                }
+
         val results = lambdaAnalyzer.analyzeAndGetLambdaReturnArguments(
             lambda,
             receiver,
             contextReceivers,
             parameters,
             expectedTypeForReturnArguments,
-            stubsForPostponedVariables,
-            candidate
+            candidate,
+            withPCLASession,
+            forOverloadByLambdaReturnType,
         )
         applyResultsOfAnalyzedLambdaToCandidateSystem(
             c,
             lambda,
             candidate,
             results,
-            completionMode,
             ::substitute
         )
         return results
@@ -151,10 +177,13 @@
         lambda: ResolvedLambdaAtom,
         candidate: Candidate,
         results: ReturnArgumentsAnalysisResult,
-        completionMode: ConstraintSystemCompletionMode,
-        substitute: (ConeKotlinType) -> ConeKotlinType = c.createSubstituteFunctorForLambdaAnalysis()
+        substitute: (ConeKotlinType) -> ConeKotlinType = c.createSubstituteFunctorForLambdaAnalysis(),
     ) {
-        val (returnArguments, inferenceSession) = results
+        val (returnArguments, additionalConstraintStorage) = results
+
+        if (additionalConstraintStorage != null) {
+            c.addOtherSystem(additionalConstraintStorage)
+        }
 
         val checkerSink: CheckerSink = CheckerSinkImpl(candidate)
         val builder = c.getBuilder()
@@ -169,11 +198,6 @@
             // If the lambda returns Unit, the last expression is not returned and should not be constrained.
             val isLastExpression = it == lastExpression
 
-            // Don't add last call for builder-inference
-            // Note, that we don't use the same condition "(returnTypeRef.type.isUnitOrFlexibleUnit || lambda.atom.shouldReturnUnit(returnArguments))"
-            // as in the if below, because "lambda.atom.shouldReturnUnit(returnArguments)" might mean that the last statement is not completed
-            if (isLastExpression && lambdaExpectedTypeIsUnit && inferenceSession is FirBuilderInferenceSession) return@forEach
-
             // TODO (KT-55837) questionable moment inherited from FE1.0 (the `haveSubsystem` case):
             //    fun <T> foo(): T
             //    run {
@@ -181,11 +205,25 @@
             //      foo() // T = Unit, even though there is no implicit return
             //    }
             //  Things get even weirder if T has an upper bound incompatible with Unit.
-            // Not calling `addSubsystemFromExpression` for builder-inference is crucial
             val haveSubsystem = c.addSubsystemFromExpression(it)
-            if (isLastExpression && !haveSubsystem &&
-                (lambdaExpectedTypeIsUnit || lambda.atom.shouldReturnUnit(returnArguments))
-            ) return@forEach
+            val isUnitLambda = lambdaExpectedTypeIsUnit || lambda.atom.shouldReturnUnit(returnArguments)
+            if (isLastExpression && isUnitLambda) {
+                // That "if" is necessary because otherwise we would force a lambda return type
+                // to be inferred from completed last expression.
+                // See `test1` at testData/diagnostics/tests/inference/coercionToUnit/afterBareReturn.kt
+                if (haveSubsystem) {
+                    // We don't force it because of the cases like
+                    // buildMap {
+                    //    put("a", 1) // While `put` returns V, we should not enforce the latter to be a subtype of Unit
+                    // }
+                    // See KT-63602 for details.
+                    builder.addSubtypeConstraintIfCompatible(
+                        it.resolvedType, returnTypeRef.type,
+                        ConeLambdaArgumentConstraintPosition(lambda.atom)
+                    )
+                }
+                return@forEach
+            }
 
             hasExpressionInReturnArguments = true
             if (!builder.hasContradiction) {
@@ -212,25 +250,6 @@
         lambda.analyzed = true
         lambda.returnStatements = returnArguments
         c.resolveForkPointsConstraints()
-
-        if (inferenceSession != null) {
-            val postponedVariables = inferenceSession.inferPostponedVariables(lambda, builder, completionMode, candidate)
-
-            if (postponedVariables == null) {
-                builder.removePostponedVariables()
-                return
-            }
-
-            for ((constructor, resultType) in postponedVariables) {
-                val variableWithConstraints = builder.currentStorage().notFixedTypeVariables[constructor] ?: continue
-                val variable = variableWithConstraints.typeVariable as ConeTypeVariable
-
-                builder.unmarkPostponedVariable(variable)
-                builder.addSubtypeConstraint(resultType, variable.defaultType(c), BuilderInferencePosition)
-            }
-
-            c.removePostponedTypeVariablesFromConstraints(postponedVariables.keys)
-        }
     }
 
     fun PostponedArgumentsAnalyzerContext.createSubstituteFunctorForLambdaAnalysis(): (ConeKotlinType) -> ConeKotlinType {
diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/FirCallCompletionResultsWriterTransformer.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/FirCallCompletionResultsWriterTransformer.kt
index d116326..b994efc 100644
--- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/FirCallCompletionResultsWriterTransformer.kt
+++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/FirCallCompletionResultsWriterTransformer.kt
@@ -9,6 +9,7 @@
 import org.jetbrains.kotlin.fakeElement
 import org.jetbrains.kotlin.fir.*
 import org.jetbrains.kotlin.fir.declarations.*
+import org.jetbrains.kotlin.fir.declarations.synthetic.FirSyntheticProperty
 import org.jetbrains.kotlin.fir.declarations.utils.isInline
 import org.jetbrains.kotlin.fir.diagnostics.ConeDiagnostic
 import org.jetbrains.kotlin.fir.diagnostics.ConeSimpleDiagnostic
@@ -22,6 +23,7 @@
 import org.jetbrains.kotlin.fir.resolve.calls.*
 import org.jetbrains.kotlin.fir.resolve.dfa.FirDataFlowAnalyzer
 import org.jetbrains.kotlin.fir.resolve.diagnostics.*
+import org.jetbrains.kotlin.fir.resolve.inference.FirStubTypeTransformer
 import org.jetbrains.kotlin.fir.resolve.inference.ResolvedLambdaAtom
 import org.jetbrains.kotlin.fir.resolve.substitution.ConeSubstitutor
 import org.jetbrains.kotlin.fir.resolve.substitution.substitutorByMap
@@ -32,7 +34,10 @@
 import org.jetbrains.kotlin.fir.scopes.impl.isWrappedIntegerOperator
 import org.jetbrains.kotlin.fir.scopes.impl.isWrappedIntegerOperatorForUnsignedType
 import org.jetbrains.kotlin.fir.symbols.FirBasedSymbol
-import org.jetbrains.kotlin.fir.symbols.impl.*
+import org.jetbrains.kotlin.fir.symbols.impl.FirCallableSymbol
+import org.jetbrains.kotlin.fir.symbols.impl.FirConstructorSymbol
+import org.jetbrains.kotlin.fir.symbols.impl.FirFunctionSymbol
+import org.jetbrains.kotlin.fir.symbols.impl.FirNamedFunctionSymbol
 import org.jetbrains.kotlin.fir.types.*
 import org.jetbrains.kotlin.fir.types.builder.buildErrorTypeRef
 import org.jetbrains.kotlin.fir.types.builder.buildStarProjection
@@ -124,7 +129,7 @@
 
         if (mode == Mode.DelegatedPropertyCompletion) {
             // Update type for `$delegateField` in `$$delegateField.get/setValue()` calls inside accessors
-            val typeUpdater = TypeUpdaterForDelegateArguments()
+            val typeUpdater = TypeUpdaterForPCLAAndDelegateReceivers()
             qualifiedAccessExpression.transformExplicitReceiver(typeUpdater, null)
         }
 
@@ -137,6 +142,12 @@
             extensionReceiver = extensionReceiver?.transformSingle(integerOperatorApproximator, expectedExtensionReceiverType)
         }
 
+        if (subCandidate.usedOuterCs) {
+            val updaterForThisReferences = TypeUpdaterForPCLAAndDelegateReceivers()
+            dispatchReceiver = dispatchReceiver?.transformSingle(updaterForThisReferences, null)
+            extensionReceiver = extensionReceiver?.transformSingle(updaterForThisReferences, null)
+        }
+
         qualifiedAccessExpression.apply {
             replaceCalleeReference(calleeReference.toResolvedReference())
             replaceDispatchReceiver(dispatchReceiver)
@@ -171,6 +182,21 @@
         if (declaration !is FirErrorFunction) {
             qualifiedAccessExpression.replaceTypeArguments(typeArguments)
         }
+
+        for (postponedCall in subCandidate.postponedPCLACalls) {
+            postponedCall.transformSingle(this, null)
+        }
+
+        for (callback in subCandidate.onCompletionResultsWritingCallbacks) {
+            callback(finalSubstitutor)
+        }
+
+        // TODO: Be aware of exponent
+        val firStubTypeTransformer = FirStubTypeTransformer(finalSubstitutor)
+        for (lambda in subCandidate.lambdasAnalyzedWithPCLA) {
+            lambda.transformSingle(firStubTypeTransformer, null)
+        }
+
         session.lookupTracker?.recordTypeResolveAsLookup(type, qualifiedAccessExpression.source, context.file.source)
         return qualifiedAccessExpression
     }
@@ -195,7 +221,7 @@
      * TODO: In future, it would be nice to get rid of it and there's actually a way to do it – not using substitution overrides (see KT-61618)
      */
     private fun Candidate.updateSubstitutedMemberIfReceiverContainsTypeVariable() {
-        val updatedSymbol = symbol.updateSubstitutedMemberIfReceiverContainsTypeVariable() ?: return
+        val updatedSymbol = symbol.updateSubstitutedMemberIfReceiverContainsTypeVariable(usedOuterCs) ?: return
         val oldSymbol = symbol
 
         @OptIn(Candidate.UpdatingSymbol::class)
@@ -221,9 +247,9 @@
         }
     }
 
-    private fun FirBasedSymbol<*>.updateSubstitutedMemberIfReceiverContainsTypeVariable(): FirBasedSymbol<*>? {
+    private fun FirBasedSymbol<*>.updateSubstitutedMemberIfReceiverContainsTypeVariable(usedOuterCs: Boolean): FirBasedSymbol<*>? {
         // TODO: Add assertion that this function returns not-null only for BI and delegation inference
-        if (mode != Mode.DelegatedPropertyCompletion) return null
+        if (mode != Mode.DelegatedPropertyCompletion && !usedOuterCs) return null
 
         val fir = fir
         if (fir !is FirCallableDeclaration) return null
@@ -240,6 +266,22 @@
             ) as? FirClassSubstitutionScope ?: return null
 
         val original = fir.originalForSubstitutionOverride ?: return null
+
+        if (fir is FirSyntheticProperty && fir.symbol is FirSimpleSyntheticPropertySymbol && original is FirSyntheticProperty) {
+            var result: FirBasedSymbol<*>? = null
+            FirSyntheticPropertiesScope.createIfSyntheticNamesProviderIsDefined(session, updatedDispatchReceiverType, scope)
+                ?.processPropertiesByName(fir.name) {
+                    val newProperty = it.fir as? FirSyntheticProperty ?: return@processPropertiesByName
+                    val originalForNew = newProperty.originalForSubstitutionOverride ?: return@processPropertiesByName
+                    if (originalForNew.getter.delegate == original.getter.delegate) {
+                        check(result == null)
+                        result = it
+                    }
+                }
+
+            return result ?: error("Not found synthetic property: ${fir.renderWithType()}")
+        }
+
         return findSingleSubstitutedSymbolWithOriginal(original.symbol) { processor ->
             when (original) {
                 is FirSimpleFunction -> scope.processFunctionsByName(original.name, processor)
@@ -519,10 +561,19 @@
             }
         }
 
+        var dispatchReceiver = subCandidate.dispatchReceiverExpression()
+        var extensionReceiver = subCandidate.chosenExtensionReceiverExpression()
+
+        if (subCandidate.usedOuterCs) {
+            val updaterForThisReferences = TypeUpdaterForPCLAAndDelegateReceivers()
+            dispatchReceiver = dispatchReceiver?.transformSingle(updaterForThisReferences, null)
+            extensionReceiver = extensionReceiver?.transformSingle(updaterForThisReferences, null)
+        }
+
         return callableReferenceAccess.apply {
             replaceCalleeReference(resolvedReference)
-            replaceDispatchReceiver(subCandidate.dispatchReceiverExpression())
-            replaceExtensionReceiver(subCandidate.chosenExtensionReceiverExpression())
+            replaceDispatchReceiver(dispatchReceiver)
+            replaceExtensionReceiver(extensionReceiver)
             if (calleeReference.candidate.doesResolutionResultOverrideOtherToPreserveCompatibility()) {
                 addNonFatalDiagnostic(ConeResolutionResultOverridesOtherToPreserveCompatibility)
             }
@@ -533,25 +584,33 @@
         return smartCastExpression.transformOriginalExpression(this, data)
     }
 
-    private inner class TypeUpdaterForDelegateArguments : FirTransformer<Any?>() {
+    private inner class TypeUpdaterForPCLAAndDelegateReceivers : FirTransformer<Any?>() {
         override fun <E : FirElement> transformElement(element: E, data: Any?): E {
             return element
         }
 
+        override fun transformThisReceiverExpression(thisReceiverExpression: FirThisReceiverExpression, data: Any?): FirStatement {
+            return transformTypeRefForQualifiedAccess(thisReceiverExpression)
+        }
+
         override fun transformQualifiedAccessExpression(
             qualifiedAccessExpression: FirQualifiedAccessExpression,
             data: Any?,
         ): FirStatement {
+            return transformTypeRefForQualifiedAccess(qualifiedAccessExpression)
+        }
+
+        override fun transformPropertyAccessExpression(propertyAccessExpression: FirPropertyAccessExpression, data: Any?): FirStatement {
+            return transformQualifiedAccessExpression(propertyAccessExpression, data)
+        }
+
+        private fun transformTypeRefForQualifiedAccess(qualifiedAccessExpression: FirQualifiedAccessExpression): FirQualifiedAccessExpression {
             val originalType = qualifiedAccessExpression.resolvedType
             val substitutedReceiverType = finallySubstituteOrNull(originalType) ?: return qualifiedAccessExpression
             qualifiedAccessExpression.replaceConeTypeOrNull(substitutedReceiverType)
             session.lookupTracker?.recordTypeResolveAsLookup(substitutedReceiverType, qualifiedAccessExpression.source, context.file.source)
             return qualifiedAccessExpression
         }
-
-        override fun transformPropertyAccessExpression(propertyAccessExpression: FirPropertyAccessExpression, data: Any?): FirStatement {
-            return transformQualifiedAccessExpression(propertyAccessExpression, data)
-        }
     }
 
     private fun FirTypeRef.substitute(candidate: Candidate): ConeKotlinType {
diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/body/resolve/BodyResolveContext.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/body/resolve/BodyResolveContext.kt
index 0b465f4..d6a5953 100644
--- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/body/resolve/BodyResolveContext.kt
+++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/body/resolve/BodyResolveContext.kt
@@ -20,12 +20,9 @@
 import org.jetbrains.kotlin.fir.resolve.calls.ImplicitExtensionReceiverValue
 import org.jetbrains.kotlin.fir.resolve.calls.ImplicitReceiverValue
 import org.jetbrains.kotlin.fir.resolve.calls.InaccessibleImplicitReceiverValue
-import org.jetbrains.kotlin.fir.resolve.calls.ResolutionContext
 import org.jetbrains.kotlin.fir.resolve.dfa.DataFlowAnalyzerContext
-import org.jetbrains.kotlin.fir.resolve.inference.FirBuilderInferenceSession
-import org.jetbrains.kotlin.fir.resolve.inference.FirCallCompleter
-import org.jetbrains.kotlin.fir.resolve.inference.FirDelegatedPropertyInferenceSession
 import org.jetbrains.kotlin.fir.resolve.inference.FirInferenceSession
+import org.jetbrains.kotlin.fir.resolve.inference.FirPCLAInferenceSession
 import org.jetbrains.kotlin.fir.resolve.transformers.ReturnTypeCalculator
 import org.jetbrains.kotlin.fir.resolve.transformers.withScopeCleanup
 import org.jetbrains.kotlin.fir.scopes.FirScope
@@ -57,6 +54,7 @@
     @PrivateForInline
     var regularTowerDataContexts = FirRegularTowerDataContexts(regular = FirTowerDataContext())
 
+    // TODO: Rename to postponed
     @PrivateForInline
     val specialTowerDataContexts = FirSpecialTowerDataContexts()
 
@@ -263,7 +261,6 @@
                 holder.scopeSession
             )
             addReceiver(labelName, receiver, additionalLabelName)
-            (inferenceSession as? FirBuilderInferenceSession)?.addLambdaImplicitReceiver(receiver)
         }
 
         f()
@@ -331,11 +328,11 @@
     }
 
     @OptIn(PrivateForInline::class)
-    inline fun <R> withInferenceSession(inferenceSession: FirInferenceSession, block: () -> R): R {
+    inline fun <R, S : FirInferenceSession> withInferenceSession(inferenceSession: S, block: S.() -> R): R {
         val oldSession = this.inferenceSession
         this.inferenceSession = inferenceSession
         return try {
-            block()
+            inferenceSession.block()
         } finally {
             this.inferenceSession = oldSession
         }
@@ -352,10 +349,17 @@
     }
 
     @PrivateForInline
-    inline fun <T> withTemporaryRegularContext(newContext: FirTowerDataContext?, f: () -> T): T {
-        if (newContext == null) return f()
+    inline fun <T> withTemporaryRegularContext(newContext: PostponedAtomsResolutionContext?, f: () -> T): T {
+        val (towerDataContext, newInferenceSession) = newContext ?: return f()
+
         return withTowerDataModeCleanup {
-            withTowerDataContexts(regularTowerDataContexts.replaceAndSetActiveRegularContext(newContext), f)
+            withTowerDataContexts(regularTowerDataContexts.replaceAndSetActiveRegularContext(towerDataContext)) {
+                if (newInferenceSession !== this.inferenceSession) {
+                    withInferenceSession(newInferenceSession) { f() }
+                } else {
+                    f()
+                }
+            }
         }
     }
 
@@ -382,9 +386,11 @@
             // to use information from local class inside it.
             // However, we should not copy other kinds of inference sessions,
             // otherwise we can "inherit" type variables from there provoking inference problems
-            if (this@BodyResolveContext.inferenceSession is FirBuilderInferenceSession) {
+            if (this@BodyResolveContext.inferenceSession is FirPCLAInferenceSession) {
                 inferenceSession = this@BodyResolveContext.inferenceSession
             }
+
+            outerConstraintStorage = this@BodyResolveContext.outerConstraintStorage
         }
 
     // withElement PUBLIC API
@@ -756,7 +762,9 @@
 
     @OptIn(PrivateForInline::class)
     fun storeContextForAnonymousFunction(anonymousFunction: FirAnonymousFunction) {
-        specialTowerDataContexts.storeAnonymousFunctionContext(anonymousFunction.symbol, towerDataContext)
+        specialTowerDataContexts.storeAnonymousFunctionContext(
+            anonymousFunction.symbol, towerDataContext, inferenceSession
+        )
     }
 
     @OptIn(PrivateForInline::class)
@@ -867,23 +875,6 @@
         }
     }
 
-    inline fun <T> forPropertyDelegateAccessors(
-        property: FirProperty,
-        resolutionContext: ResolutionContext,
-        callCompleter: FirCallCompleter,
-        f: FirDelegatedPropertyInferenceSession.() -> T
-    ) {
-        val inferenceSession = FirDelegatedPropertyInferenceSession(
-            property,
-            resolutionContext,
-            callCompleter.createPostponedArgumentsAnalyzer(resolutionContext)
-        )
-
-        withInferenceSession(inferenceSession) {
-            inferenceSession.f()
-        }
-    }
-
     @OptIn(PrivateForInline::class)
     inline fun <T> withOuterConstraintStorage(
         storage: ConstraintStorage,
@@ -956,7 +947,8 @@
     fun storeCallableReferenceContext(callableReferenceAccess: FirCallableReferenceAccess) {
         specialTowerDataContexts.storeCallableReferenceContext(
             callableReferenceAccess,
-            towerDataContext.createSnapshot(keepMutable = false)
+            towerDataContext.createSnapshot(keepMutable = false),
+            inferenceSession,
         )
     }
 
diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/body/resolve/FirDeclarationsResolveTransformer.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/body/resolve/FirDeclarationsResolveTransformer.kt
index fab1578..5f26389 100644
--- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/body/resolve/FirDeclarationsResolveTransformer.kt
+++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/body/resolve/FirDeclarationsResolveTransformer.kt
@@ -38,7 +38,6 @@
 import org.jetbrains.kotlin.fir.resolve.inference.extractLambdaInfoFromFunctionType
 import org.jetbrains.kotlin.fir.resolve.substitution.ChainedSubstitutor
 import org.jetbrains.kotlin.fir.resolve.substitution.ConeSubstitutor
-import org.jetbrains.kotlin.fir.resolve.transformers.FirCallCompletionResultsWriterTransformer
 import org.jetbrains.kotlin.fir.resolve.transformers.FirStatusResolver
 import org.jetbrains.kotlin.fir.resolve.transformers.contracts.runContractResolveForFunction
 import org.jetbrains.kotlin.fir.resolve.transformers.transformVarargTypeToArrayType
@@ -329,89 +328,82 @@
 
     private fun transformPropertyAccessorsWithDelegate(
         property: FirProperty,
-        delegate: FirExpression,
+        delegateContainer: FirExpression,
         shouldResolveEverything: Boolean,
     ) {
-        val isImplicitTypedProperty = property.returnTypeRef is FirImplicitTypeRef
+        require(delegateContainer is FirWrappedDelegateExpression)
+        dataFlowAnalyzer.enterDelegateExpression()
 
-        context.forPropertyDelegateAccessors(property, resolutionContext, callCompleter) {
-            dataFlowAnalyzer.enterDelegateExpression()
-            // Resolve delegate expression, after that, delegate will contain either expr.provideDelegate or expr
+        // First, resolve delegate expression in dependent context withing existing (possibly Default) inference session
+        val delegateExpression =
+            // Resolve delegate expression; after that, delegate will contain either expr.provideDelegate or expr
             if (property.isLocal) {
-                property.transformDelegate(transformer, ResolutionMode.ContextDependent.Delegate)
+                transformDelegateExpression(delegateContainer)
             } else {
                 context.forPropertyInitializer {
-                    property.transformDelegate(transformer, ResolutionMode.ContextDependent.Delegate)
+                    transformDelegateExpression(delegateContainer)
                 }
             }
 
-            // We don't use inference from setValue calls (i.e. don't resolve setters until the delegate inference is completed),
-            // when property doesn't have explicit type.
+        // Then, create a root/child inference session based on the resolved delegate expression
+        context.withInferenceSession(
+            FirDelegatedPropertyInferenceSession(
+                resolutionContext,
+                callCompleter,
+                delegateExpression,
+            )
+        ) {
+            property.replaceDelegate(
+                getResolvedProvideDelegateIfSuccessful(delegateContainer.provideDelegateCall, delegateExpression)
+                    ?: delegateExpression
+            )
+
+            // We don't use inference from setValue calls (i.e., don't resolve setters until the delegate inference is completed)
+            // when the property doesn't have an explicit type.
             // It's necessary because we need to supply the property type as the 3rd argument for `setValue` and there might be uninferred
             // variables from `getValue`.
             // The same logic was used at K1 (see org.jetbrains.kotlin.resolve.DelegatedPropertyResolver.inferDelegateTypeFromGetSetValueMethods)
+            val isImplicitTypedProperty = property.returnTypeRef is FirImplicitTypeRef
             property.transformAccessors(
                 if (isImplicitTypedProperty) SetterResolutionMode.SKIP else SetterResolutionMode.FULLY_RESOLVE,
                 shouldResolveEverything,
             )
 
-            val completedCalls = completeCandidates()
-
-            val finalSubstitutor = createFinalSubstitutor()
-
-            finalSubstitutor.substituteOrNull(property.returnTypeRef.coneType)?.let { substitutedType ->
-                property.replaceReturnTypeRef(property.returnTypeRef.withReplacedConeType(substitutedType))
-            }
-            property.getter?.transformTypeWithPropertyType(property.returnTypeRef, forceUpdateForNonImplicitTypes = true)
-            property.setter?.transformTypeWithPropertyType(property.returnTypeRef, forceUpdateForNonImplicitTypes = true)
-            property.replaceReturnTypeRef(
-                property.returnTypeRef.approximateDeclarationType(
-                    session,
-                    property.visibilityForApproximation(),
-                    property.isLocal
+            completeSessionOrPostponeIfNonRoot { finalSubstitutor ->
+                finalSubstitutor.substituteOrNull(property.returnTypeRef.coneType)?.let { substitutedType ->
+                    property.replaceReturnTypeRef(property.returnTypeRef.withReplacedConeType(substitutedType))
+                }
+                property.getter?.transformTypeWithPropertyType(property.returnTypeRef, forceUpdateForNonImplicitTypes = true)
+                property.setter?.transformTypeWithPropertyType(property.returnTypeRef, forceUpdateForNonImplicitTypes = true)
+                property.replaceReturnTypeRef(
+                    property.returnTypeRef.approximateDeclarationType(
+                        session,
+                        property.visibilityForApproximation(),
+                        property.isLocal
+                    )
                 )
-            )
 
-            val callCompletionResultsWriter = callCompleter.createCompletionResultsWriter(
-                finalSubstitutor,
-                mode = FirCallCompletionResultsWriterTransformer.Mode.DelegatedPropertyCompletion
-            )
-            completedCalls.forEach {
-                it.transformSingle(callCompletionResultsWriter, null)
+                // `isImplicitTypedProperty` means we haven't run setter resolution yet (see its second usage)
+                if (isImplicitTypedProperty) {
+                    property.resolveSetter(mayResolveSetterBody = true, shouldResolveEverything = shouldResolveEverything)
+                }
+
+                dataFlowAnalyzer.exitDelegateExpression(delegateContainer)
             }
         }
-
-        // `isImplicitTypedProperty` means we haven't run setter resolution yet (see its second usage)
-        if (isImplicitTypedProperty) {
-            property.resolveSetter(mayResolveSetterBody = true, shouldResolveEverything = shouldResolveEverything)
-        }
-
-        dataFlowAnalyzer.exitDelegateExpression(delegate)
     }
 
-    override fun transformPropertyAccessor(propertyAccessor: FirPropertyAccessor, data: ResolutionMode): FirPropertyAccessor {
-        return propertyAccessor.also {
-            transformProperty(it.propertySymbol.fir, data)
-        }
-    }
-
-    override fun transformWrappedDelegateExpression(
-        wrappedDelegateExpression: FirWrappedDelegateExpression,
-        data: ResolutionMode,
-    ): FirStatement {
-        // First, resolve delegate expression in dependent context, and add potentially partially resolved call to inference session
-        // (that is why we use ResolutionMode.ContextDependent.Delegate instead of plain ContextDependent)
-        val delegateExpression = wrappedDelegateExpression.expression.transformSingle(transformer, ResolutionMode.ContextDependent.Delegate)
-            .transformSingle(components.integerLiteralAndOperatorApproximationTransformer, null)
-
-        val provideDelegateCall = wrappedDelegateExpression.provideDelegateCall
-        provideDelegateCall.replaceExplicitReceiver(delegateExpression)
+    private fun getResolvedProvideDelegateIfSuccessful(
+        provideDelegateCall: FirFunctionCall,
+        resolvedDelegateExpression: FirExpression,
+    ): FirFunctionCall? {
+        provideDelegateCall.replaceExplicitReceiver(resolvedDelegateExpression)
 
         // Resolve call for provideDelegate, without completion
         // TODO: this generates some nodes in the control flow graph which we don't want if we
         //  end up not selecting this option, KT-59684
         transformer.expressionsTransformer?.transformFunctionCallInternal(
-            provideDelegateCall, ResolutionMode.ContextDependent, provideDelegate = true
+            provideDelegateCall, ResolutionMode.ReceiverResolution, provideDelegate = true
         )
 
         // If we got successful candidate for provideDelegate, let's select it
@@ -438,10 +430,19 @@
             return provideDelegateCall
         }
 
-        // Select delegate expression otherwise
-        return delegateExpression
+        return null
     }
 
+    override fun transformPropertyAccessor(propertyAccessor: FirPropertyAccessor, data: ResolutionMode): FirPropertyAccessor {
+        return propertyAccessor.also {
+            transformProperty(it.propertySymbol.fir, data)
+        }
+    }
+
+    private fun transformDelegateExpression(delegate: FirWrappedDelegateExpression): FirExpression =
+        delegate.expression.transformSingle(transformer, ResolutionMode.ContextDependent.Delegate)
+            .transformSingle(components.integerLiteralAndOperatorApproximationTransformer, null)
+
     /**
      * For supporting the case when `provideDelegate` has a signature with type variable as a return type, like
      *  fun <K> K.provideDelegate(receiver: Any?, property: kotlin.reflect.KProperty<*>): K = this
@@ -481,11 +482,6 @@
 
         val candidateSystem = candidate.system
         val candidateStorage = candidateSystem.currentStorage()
-        val allTypeVariables = candidateStorage.allTypeVariables.keys.toList()
-        // Subset of type variables obtained from the `provideDelegate` call
-        val typeVariablesRelatedToProvideDelegate =
-            allTypeVariables.subList(candidateStorage.outerSystemVariablesPrefixSize, allTypeVariables.size).toSet()
-
         val variableWithConstraints =
             candidateSystem.notFixedTypeVariables[typeVariable] ?: error("Not found type variable $typeVariable")
 
@@ -494,7 +490,7 @@
         // Temporary declare all the "outer" variables as proper (i.e., all inner variables as improper)
         // Without that, all variables (both inner and outer ones) would be considered as improper,
         // while we want to fix to assume `Delegate<Tv>` as proper because `Tv` belongs to the outer system
-        candidateSystem.withTypeVariablesThatAreNotCountedAsProperTypes(typeVariablesRelatedToProvideDelegate) {
+        candidateSystem.withTypeVariablesThatAreCountedAsProperTypes(candidateSystem.outerTypeVariables.orEmpty()) {
             // TODO: reconsider the approach here (KT-61781 for tracking)
             // Actually, this code might fail with an exception in some rare cases (see KT-61781)
             // The problem is that in the issue example, when fixing T type variable, it has two upper bounds: X and Delegate<Y>
@@ -506,7 +502,7 @@
             // it seems like they do something relevant.
             resultType = inferenceComponents.resultTypeResolver.findResultTypeOrNull(
                 candidateSystem, variableWithConstraints, TypeVariableDirectionCalculator.ResolveDirection.UNKNOWN
-            ) as? ConeKotlinType ?: return@withTypeVariablesThatAreNotCountedAsProperTypes
+            ) as? ConeKotlinType ?: return@withTypeVariablesThatAreCountedAsProperTypes
 
 
             check(!candidateStorage.hasContradiction) { "We only should try fixing variables on successful provideDelegate candidate" }
diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/body/resolve/FirExpressionsResolveTransformer.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/body/resolve/FirExpressionsResolveTransformer.kt
index 34533b8..a284fc5 100644
--- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/body/resolve/FirExpressionsResolveTransformer.kt
+++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/body/resolve/FirExpressionsResolveTransformer.kt
@@ -31,7 +31,6 @@
 import org.jetbrains.kotlin.fir.resolve.*
 import org.jetbrains.kotlin.fir.resolve.calls.*
 import org.jetbrains.kotlin.fir.resolve.diagnostics.*
-import org.jetbrains.kotlin.fir.resolve.inference.FirStubInferenceSession
 import org.jetbrains.kotlin.fir.resolve.substitution.ConeSubstitutor
 import org.jetbrains.kotlin.fir.resolve.transformers.replaceLambdaArgumentInvocationKinds
 import org.jetbrains.kotlin.fir.scopes.impl.isWrappedIntegerOperator
@@ -91,7 +90,7 @@
         transformQualifiedAccessExpression(qualifiedAccessExpression, data, isUsedAsReceiver = false, isUsedAsGetClassReceiver = false)
     }
 
-    fun transformQualifiedAccessExpression(
+    private fun transformQualifiedAccessExpression(
         qualifiedAccessExpression: FirQualifiedAccessExpression,
         data: ResolutionMode,
         isUsedAsReceiver: Boolean,
@@ -1035,6 +1034,13 @@
             ),
         )
 
+        // for cases like
+        // buildSomething { tVar = "" // Should infer TV from String assignment }
+        context.inferenceSession.addSubtypeConstraintIfCompatible(
+            variableAssignment.lValue.resolvedType, variableAssignment.rValue.resolvedType,
+            variableAssignment,
+        )
+
         dataFlowAnalyzer.exitVariableAssignment(result)
 
         return result
@@ -1053,10 +1059,10 @@
         val transformedLHS = when (explicitReceiver) {
             is FirPropertyAccessExpression ->
                 transformQualifiedAccessExpression(
-                    explicitReceiver, ResolutionMode.ContextIndependent, isUsedAsReceiver = true, isUsedAsGetClassReceiver = false
+                    explicitReceiver, ResolutionMode.ReceiverResolution.ForCallableReference, isUsedAsReceiver = true, isUsedAsGetClassReceiver = false
                 ) as FirExpression
             else ->
-                explicitReceiver?.transformSingle(this, ResolutionMode.ContextIndependent)
+                explicitReceiver?.transformSingle(this, ResolutionMode.ReceiverResolution.ForCallableReference)
         }.apply {
             if (this is FirResolvedQualifier && callableReferenceAccess.hasQuestionMarkAtLHS) {
                 replaceIsNullableLHSForCallableReference(true)
@@ -1181,7 +1187,7 @@
                         constExpression.replaceKind(expressionType.toConstKind() as ConstantValueKind<T>)
                         expressionType
                     }
-                    data is ResolutionMode.ReceiverResolution -> {
+                    data is ResolutionMode.ReceiverResolution && !data.forCallableReference -> {
                         require(expressionType is ConeIntegerLiteralConstantTypeImpl)
                         ConeIntegerConstantOperatorTypeImpl(expressionType.isUnsigned, ConeNullability.NOT_NULL)
                     }
diff --git a/compiler/fir/semantics/src/org/jetbrains/kotlin/fir/resolve/calls/ResolutionDiagnostic.kt b/compiler/fir/semantics/src/org/jetbrains/kotlin/fir/resolve/calls/ResolutionDiagnostic.kt
index e1854f3..7e4e9d3 100644
--- a/compiler/fir/semantics/src/org/jetbrains/kotlin/fir/resolve/calls/ResolutionDiagnostic.kt
+++ b/compiler/fir/semantics/src/org/jetbrains/kotlin/fir/resolve/calls/ResolutionDiagnostic.kt
@@ -7,6 +7,7 @@
 
 import org.jetbrains.kotlin.KtSourceElement
 import org.jetbrains.kotlin.fir.declarations.FirFunction
+import org.jetbrains.kotlin.fir.declarations.FirTypeParameter
 import org.jetbrains.kotlin.fir.declarations.FirValueParameter
 import org.jetbrains.kotlin.fir.expressions.FirExpression
 import org.jetbrains.kotlin.fir.expressions.FirNamedArgumentExpression
@@ -15,7 +16,6 @@
 import org.jetbrains.kotlin.fir.symbols.impl.FirConstructorSymbol
 import org.jetbrains.kotlin.fir.symbols.impl.FirNamedFunctionSymbol
 import org.jetbrains.kotlin.fir.symbols.impl.FirPropertySymbol
-import org.jetbrains.kotlin.fir.symbols.impl.FirTypeParameterSymbol
 import org.jetbrains.kotlin.fir.types.ConeKotlinType
 import org.jetbrains.kotlin.fir.types.ConeTypeVariable
 import org.jetbrains.kotlin.resolve.ForbiddenNamedArgumentsTarget
@@ -158,6 +158,7 @@
 
 object TypeParameterAsExpression : ResolutionDiagnostic(INAPPLICABLE)
 
-class StubBuilderInferenceReceiver(
-    val typeParameterSymbol: FirTypeParameterSymbol
-) : ResolutionDiagnostic(RESOLVED_WITH_ERROR)
+class TypeVariableAsExplicitReceiver(
+    val explicitReceiver: FirExpression,
+    val typeParameter: FirTypeParameter,
+) : ResolutionDiagnostic(RESOLVED_WITH_ERROR)
\ No newline at end of file
diff --git a/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/types/IrTypeSystemContext.kt b/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/types/IrTypeSystemContext.kt
index a0182e0..a035cc4 100644
--- a/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/types/IrTypeSystemContext.kt
+++ b/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/types/IrTypeSystemContext.kt
@@ -596,6 +596,11 @@
         return IrTypeSubstitutor(emptyMap())
     }
 
+    @K2Only
+    override fun createSubstitutionFromSubtypingStubTypesToTypeVariables(): TypeSubstitutorMarker {
+        error("Only for inference")
+    }
+
     override fun TypeSubstitutorMarker.safeSubstitute(type: KotlinTypeMarker): KotlinTypeMarker {
         require(this is AbstractIrTypeSubstitutor)
         require(type is IrType)
diff --git a/compiler/resolution.common/src/org/jetbrains/kotlin/resolve/calls/components/PostponedArgumentsAnalyzerContext.kt b/compiler/resolution.common/src/org/jetbrains/kotlin/resolve/calls/components/PostponedArgumentsAnalyzerContext.kt
index 68cd259..d54f073 100644
--- a/compiler/resolution.common/src/org/jetbrains/kotlin/resolve/calls/components/PostponedArgumentsAnalyzerContext.kt
+++ b/compiler/resolution.common/src/org/jetbrains/kotlin/resolve/calls/components/PostponedArgumentsAnalyzerContext.kt
@@ -7,10 +7,13 @@
 
 import org.jetbrains.kotlin.resolve.calls.inference.ConstraintSystemBuilder
 import org.jetbrains.kotlin.resolve.calls.inference.model.ConstraintStorage
+import org.jetbrains.kotlin.resolve.calls.inference.model.VariableWithConstraints
 import org.jetbrains.kotlin.types.model.*
 
 interface PostponedArgumentsAnalyzerContext : TypeSystemInferenceExtensionContext {
-    fun buildCurrentSubstitutor(additionalBindings: Map<TypeConstructorMarker, StubTypeMarker>): TypeSubstitutorMarker
+    val notFixedTypeVariables: Map<TypeConstructorMarker, VariableWithConstraints>
+
+    fun buildCurrentSubstitutor(additionalBindings: Map<TypeConstructorMarker, KotlinTypeMarker>): TypeSubstitutorMarker
     fun buildNotFixedVariablesToStubTypesSubstitutor(): TypeSubstitutorMarker
     fun bindingStubsForPostponedVariables(): Map<TypeVariableMarker, StubTypeMarker>
 
diff --git a/compiler/resolution.common/src/org/jetbrains/kotlin/resolve/calls/inference/components/ConstraintSystemCompletionContext.kt b/compiler/resolution.common/src/org/jetbrains/kotlin/resolve/calls/inference/components/ConstraintSystemCompletionContext.kt
index 8276138..9adfaff 100644
--- a/compiler/resolution.common/src/org/jetbrains/kotlin/resolve/calls/inference/components/ConstraintSystemCompletionContext.kt
+++ b/compiler/resolution.common/src/org/jetbrains/kotlin/resolve/calls/inference/components/ConstraintSystemCompletionContext.kt
@@ -11,21 +11,16 @@
 import org.jetbrains.kotlin.resolve.calls.inference.model.*
 import org.jetbrains.kotlin.resolve.calls.model.PostponedAtomWithRevisableExpectedType
 import org.jetbrains.kotlin.resolve.calls.model.PostponedResolvedAtomMarker
+import org.jetbrains.kotlin.types.model.K2Only
 import org.jetbrains.kotlin.types.model.KotlinTypeMarker
 import org.jetbrains.kotlin.types.model.TypeConstructorMarker
 import org.jetbrains.kotlin.types.model.TypeVariableMarker
 
 abstract class ConstraintSystemCompletionContext : VariableFixationFinder.Context, ResultTypeResolver.Context {
-    abstract val allTypeVariables: Map<TypeConstructorMarker, TypeVariableMarker>
     abstract override val notFixedTypeVariables: Map<TypeConstructorMarker, VariableWithConstraints>
     abstract override val fixedTypeVariables: Map<TypeConstructorMarker, KotlinTypeMarker>
     abstract override val postponedTypeVariables: List<TypeVariableMarker>
 
-    /**
-     * See [org.jetbrains.kotlin.resolve.calls.inference.model.ConstraintStorage.outerSystemVariablesPrefixSize]
-     */
-    abstract val outerSystemVariablesPrefixSize: Int
-
     abstract fun getBuilder(): ConstraintSystemBuilder
 
     // type can be proper if it not contains not fixed type variables
@@ -69,7 +64,7 @@
         completionMode: ConstraintSystemCompletionMode,
         analyze: (A) -> Unit
     ): Boolean {
-        if (completionMode == ConstraintSystemCompletionMode.FULL) {
+        if (completionMode.allLambdasShouldBeAnalyzed) {
             val argumentWithTypeVariableAsExpectedType = findPostponedArgumentWithRevisableExpectedType(postponedArguments)
 
             if (argumentWithTypeVariableAsExpectedType != null) {
@@ -120,4 +115,11 @@
                 !it.typeConstructor().isClassTypeConstructor() && !it.typeConstructor().isTypeParameterTypeConstructor()
             }
         }.map { it.type }
+
+    /**
+     * @see [org.jetbrains.kotlin.resolve.calls.inference.components.VariableFixationFinder.Context.typeVariablesThatAreNotCountedAsProperTypes]
+     * @see [org.jetbrains.kotlin.fir.resolve.transformers.body.resolve.FirDeclarationsResolveTransformer.fixInnerVariablesForProvideDelegateIfNeeded]
+     */
+    @K2Only
+    abstract fun <R> withTypeVariablesThatAreCountedAsProperTypes(typeVariables: Set<TypeConstructorMarker>, block: () -> R): R
 }
diff --git a/compiler/resolution.common/src/org/jetbrains/kotlin/resolve/calls/inference/components/ConstraintSystemCompletionMode.kt b/compiler/resolution.common/src/org/jetbrains/kotlin/resolve/calls/inference/components/ConstraintSystemCompletionMode.kt
index 679c171..62dea37 100644
--- a/compiler/resolution.common/src/org/jetbrains/kotlin/resolve/calls/inference/components/ConstraintSystemCompletionMode.kt
+++ b/compiler/resolution.common/src/org/jetbrains/kotlin/resolve/calls/inference/components/ConstraintSystemCompletionMode.kt
@@ -5,8 +5,9 @@
 
 package org.jetbrains.kotlin.resolve.calls.inference.components
 
-enum class ConstraintSystemCompletionMode {
-    FULL,
+enum class ConstraintSystemCompletionMode(val allLambdasShouldBeAnalyzed: Boolean) {
+    FULL(true),
+    PCLA_POSTPONED_CALL(true),
 
     /**
      * This mode allows us to infer variables in calls, which have enough type-info to be completed right-away
@@ -18,6 +19,6 @@
      * x.plus(run { x }) // Here, to select plus overload we need to analyze lambda
      * ```
      */
-    PARTIAL,
-    UNTIL_FIRST_LAMBDA
+    PARTIAL(false),
+    UNTIL_FIRST_LAMBDA(false),
 }
diff --git a/compiler/resolution.common/src/org/jetbrains/kotlin/resolve/calls/inference/components/PostponedArgumentInputTypesResolver.kt b/compiler/resolution.common/src/org/jetbrains/kotlin/resolve/calls/inference/components/PostponedArgumentInputTypesResolver.kt
index 79d1edb..08219b5 100644
--- a/compiler/resolution.common/src/org/jetbrains/kotlin/resolve/calls/inference/components/PostponedArgumentInputTypesResolver.kt
+++ b/compiler/resolution.common/src/org/jetbrains/kotlin/resolve/calls/inference/components/PostponedArgumentInputTypesResolver.kt
@@ -9,8 +9,13 @@
 import org.jetbrains.kotlin.builtins.functions.isBasicFunctionOrKFunction
 import org.jetbrains.kotlin.config.LanguageFeature
 import org.jetbrains.kotlin.config.LanguageVersionSettings
-import org.jetbrains.kotlin.resolve.calls.inference.model.*
-import org.jetbrains.kotlin.resolve.calls.model.*
+import org.jetbrains.kotlin.resolve.calls.inference.model.Constraint
+import org.jetbrains.kotlin.resolve.calls.inference.model.ConstraintKind
+import org.jetbrains.kotlin.resolve.calls.inference.model.VariableWithConstraints
+import org.jetbrains.kotlin.resolve.calls.model.LambdaWithTypeVariableAsExpectedTypeMarker
+import org.jetbrains.kotlin.resolve.calls.model.PostponedAtomWithRevisableExpectedType
+import org.jetbrains.kotlin.resolve.calls.model.PostponedCallableReferenceMarker
+import org.jetbrains.kotlin.resolve.calls.model.PostponedResolvedAtomMarker
 import org.jetbrains.kotlin.types.model.*
 import org.jetbrains.kotlin.utils.SmartSet
 import java.util.*
@@ -531,24 +536,39 @@
         postponedArguments: List<PostponedResolvedAtomMarker>,
         topLevelType: KotlinTypeMarker,
         dependencyProvider: TypeVariableDependencyInformationProvider,
-        resolvedAtomProvider: ResolvedAtomProvider
-    ): Boolean = with(c) {
-        val expectedType = argument.run { (this as? PostponedAtomWithRevisableExpectedType)?.revisedExpectedType ?: expectedType }
+        resolvedAtomProvider: ResolvedAtomProvider,
+    ): Boolean {
+        val expectedType = argument.expectedFunctionType(c) ?: return false
 
-        if (expectedType != null && expectedType.isFunctionOrKFunctionWithAnySuspendability()) {
-            val wasFixedSomeVariable = c.fixNextReadyVariableForParameterType(
-                expectedType,
-                postponedArguments,
-                topLevelType,
-                dependencyProvider,
-                resolvedAtomProvider
-            )
+        return c.fixNextReadyVariableForParameterType(
+            expectedType,
+            postponedArguments,
+            topLevelType,
+            dependencyProvider,
+            resolvedAtomProvider,
+        )
+    }
 
-            if (wasFixedSomeVariable)
-                return true
-        }
+    fun findNextVariableForReportingNotInferredInputType(
+        c: Context,
+        argument: PostponedResolvedAtomMarker,
+        postponedArguments: List<PostponedResolvedAtomMarker>,
+        topLevelType: KotlinTypeMarker,
+        dependencyProvider: TypeVariableDependencyInformationProvider,
+    ): VariableFixationFinder.VariableForFixation? {
+        val expectedType = argument.expectedFunctionType(c) ?: return null
 
-        return false
+        return c.findNextVariableForParameterType(
+            expectedType,
+            dependencyProvider,
+            postponedArguments,
+            topLevelType,
+        )
+    }
+
+    private fun PostponedResolvedAtomMarker.expectedFunctionType(c: Context): KotlinTypeMarker? = with(c) {
+        val expectedType = (this@expectedFunctionType as? PostponedAtomWithRevisableExpectedType)?.revisedExpectedType ?: expectedType
+        expectedType?.takeIf { it.isFunctionOrKFunctionWithAnySuspendability() }
     }
 
     private fun Context.fixNextReadyVariableForParameterType(
@@ -558,17 +578,9 @@
         dependencyProvider: TypeVariableDependencyInformationProvider,
         resolvedAtomByTypeVariableProvider: ResolvedAtomProvider,
     ): Boolean = with(resolutionTypeSystemContext) {
-        val relatedVariables = type.extractArgumentsForFunctionTypeOrSubtype()
-            .flatMap { getAllDeeplyRelatedTypeVariables(it, dependencyProvider) }
-        val variableForFixation = variableFixationFinder.findFirstVariableForFixation(
-            this@fixNextReadyVariableForParameterType,
-            relatedVariables,
-            postponedArguments,
-            ConstraintSystemCompletionMode.FULL,
-            topLevelType
-        )
+        val variableForFixation = findNextVariableForParameterType(type, dependencyProvider, postponedArguments, topLevelType)
 
-        if (variableForFixation == null || !variableForFixation.hasProperConstraint)
+        if (variableForFixation == null || !variableForFixation.isReady)
             return false
 
         val variableWithConstraints = notFixedTypeVariables.getValue(variableForFixation.variable)
@@ -589,6 +601,26 @@
         return true
     }
 
+    private fun Context.findNextVariableForParameterType(
+        type: KotlinTypeMarker,
+        dependencyProvider: TypeVariableDependencyInformationProvider,
+        postponedArguments: List<PostponedResolvedAtomMarker>,
+        topLevelType: KotlinTypeMarker,
+    ): VariableFixationFinder.VariableForFixation? {
+        val outerTypeVariables = outerTypeVariables.orEmpty()
+        val relatedVariables = type.extractArgumentsForFunctionTypeOrSubtype()
+            .flatMap { getAllDeeplyRelatedTypeVariables(it, dependencyProvider) }
+            .filter { it !in outerTypeVariables }
+
+        return variableFixationFinder.findFirstVariableForFixation(
+            this,
+            relatedVariables,
+            postponedArguments,
+            ConstraintSystemCompletionMode.FULL,
+            topLevelType,
+        )
+    }
+
     private fun KotlinTypeMarker?.wrapToTypeWithKind() = this?.let { TypeWithKind(it) }
 
     companion object {
diff --git a/compiler/resolution.common/src/org/jetbrains/kotlin/resolve/calls/inference/components/ResultTypeResolver.kt b/compiler/resolution.common/src/org/jetbrains/kotlin/resolve/calls/inference/components/ResultTypeResolver.kt
index de12ac5..ebef7dc 100644
--- a/compiler/resolution.common/src/org/jetbrains/kotlin/resolve/calls/inference/components/ResultTypeResolver.kt
+++ b/compiler/resolution.common/src/org/jetbrains/kotlin/resolve/calls/inference/components/ResultTypeResolver.kt
@@ -22,6 +22,8 @@
     private val languageVersionSettings: LanguageVersionSettings
 ) {
     interface Context : TypeSystemInferenceExtensionContext {
+        val notFixedTypeVariables: Map<TypeConstructorMarker, VariableWithConstraints>
+        val outerSystemVariablesPrefixSize: Int
         fun isProperType(type: KotlinTypeMarker): Boolean
         fun buildNotFixedVariablesToStubTypesSubstitutor(): TypeSubstitutorMarker
         fun isReified(variable: TypeVariableMarker): Boolean
@@ -220,6 +222,10 @@
 
                 if (typesWithoutStubs.isNotEmpty()) {
                     commonSuperType = computeCommonSuperType(typesWithoutStubs)
+                } else if (outerSystemVariablesPrefixSize > 0) {
+                    // outerSystemVariablesPrefixSize > 0 only for PCLA (K2)
+                    @OptIn(K2Only::class)
+                    commonSuperType = createSubstitutionFromSubtypingStubTypesToTypeVariables().safeSubstitute(commonSuperType)
                 }
             }
 
@@ -273,6 +279,14 @@
         }
 
         if (!atLeastOneProper) return emptyList()
+
+        // PCLA slow path
+        // We only allow using TVs fixation for nested PCLA calls
+        if (outerSystemVariablesPrefixSize > 0) {
+            val notFixedToStubTypesSubstitutor = buildNotFixedVariablesToStubTypesSubstitutor()
+            return lowerConstraintTypes.map { notFixedToStubTypesSubstitutor.safeSubstitute(it) }
+        }
+
         if (!atLeastOneNonProper) return lowerConstraintTypes
 
         val notFixedToStubTypesSubstitutor = buildNotFixedVariablesToStubTypesSubstitutor()
@@ -336,7 +350,7 @@
     }
 
     private fun Context.isProperTypeForFixation(type: KotlinTypeMarker): Boolean =
-        isProperTypeForFixation(type) { isProperType(it) }
+        isProperTypeForFixation(type, notFixedTypeVariables.keys) { isProperType(it) }
 
     private fun findResultIfThereIsEqualsConstraint(c: Context, variableWithConstraints: VariableWithConstraints): KotlinTypeMarker? =
         with(c) {
diff --git a/compiler/resolution.common/src/org/jetbrains/kotlin/resolve/calls/inference/components/TypeVariableDependencyInformationProvider.kt b/compiler/resolution.common/src/org/jetbrains/kotlin/resolve/calls/inference/components/TypeVariableDependencyInformationProvider.kt
index 7b7ae72..0d502eb 100644
--- a/compiler/resolution.common/src/org/jetbrains/kotlin/resolve/calls/inference/components/TypeVariableDependencyInformationProvider.kt
+++ b/compiler/resolution.common/src/org/jetbrains/kotlin/resolve/calls/inference/components/TypeVariableDependencyInformationProvider.kt
@@ -7,15 +7,22 @@
 
 import org.jetbrains.kotlin.resolve.calls.inference.model.VariableWithConstraints
 import org.jetbrains.kotlin.resolve.calls.model.PostponedResolvedAtomMarker
-import org.jetbrains.kotlin.types.model.*
+import org.jetbrains.kotlin.types.model.KotlinTypeMarker
+import org.jetbrains.kotlin.types.model.TypeConstructorMarker
+import org.jetbrains.kotlin.types.model.freshTypeConstructor
+import org.jetbrains.kotlin.types.model.typeConstructor
 import org.jetbrains.kotlin.utils.SmartSet
 
 class TypeVariableDependencyInformationProvider(
     private val notFixedTypeVariables: Map<TypeConstructorMarker, VariableWithConstraints>,
     private val postponedKtPrimitives: List<PostponedResolvedAtomMarker>,
     private val topLevelType: KotlinTypeMarker?,
-    private val typeSystemContext: TypeSystemInferenceExtensionContext
+    private val typeSystemContext: VariableFixationFinder.Context
 ) {
+
+    private val outerTypeVariables: Set<TypeConstructorMarker>? =
+        typeSystemContext.outerTypeVariables
+
     /*
      * Not oriented edges
      * TypeVariable(A) has UPPER(Function1<TypeVariable(B), R>) => A and B are related deeply
@@ -41,7 +48,16 @@
         computeRelatedToTopLevelType()
     }
 
-    fun isVariableRelatedToTopLevelType(variable: TypeConstructorMarker) = relatedToTopLevelType.contains(variable)
+    fun isVariableRelatedToTopLevelType(variable: TypeConstructorMarker) =
+        relatedToTopLevelType.contains(variable)
+
+
+    fun isRelatedToOuterTypeVariable(variable: TypeConstructorMarker): Boolean {
+        val outerTypeVariables = outerTypeVariables ?: return false
+        val myDependent = getDeeplyDependentVariables(variable) ?: return false
+        return myDependent.any { it in outerTypeVariables }
+    }
+
     fun isVariableRelatedToAnyOutputType(variable: TypeConstructorMarker) = relatedToAllOutputTypes.contains(variable)
 
     fun getDeeplyDependentVariables(variable: TypeConstructorMarker) = deepTypeVariableDependencies[variable]
diff --git a/compiler/resolution.common/src/org/jetbrains/kotlin/resolve/calls/inference/components/VariableFixationFinder.kt b/compiler/resolution.common/src/org/jetbrains/kotlin/resolve/calls/inference/components/VariableFixationFinder.kt
index 8eb18a9..24cb73c 100644
--- a/compiler/resolution.common/src/org/jetbrains/kotlin/resolve/calls/inference/components/VariableFixationFinder.kt
+++ b/compiler/resolution.common/src/org/jetbrains/kotlin/resolve/calls/inference/components/VariableFixationFinder.kt
@@ -27,25 +27,40 @@
         val fixedTypeVariables: Map<TypeConstructorMarker, KotlinTypeMarker>
         val postponedTypeVariables: List<TypeVariableMarker>
         val constraintsFromAllForkPoints: MutableList<Pair<IncorporationConstraintPosition, ForkPointData>>
+        val allTypeVariables: Map<TypeConstructorMarker, TypeVariableMarker>
 
         /**
-         * If not null, that property means that we should assume temporary
-         * `allTypeVariables.keys.minus(typeVariablesThatAreNotCountedAsProperTypes)` as proper types when fixating some variables.
+         * See [org.jetbrains.kotlin.resolve.calls.inference.model.ConstraintStorage.outerSystemVariablesPrefixSize]
+         */
+        val outerSystemVariablesPrefixSize: Int
+
+        val outerTypeVariables: Set<TypeConstructorMarker>?
+            get() =
+                when {
+                    outerSystemVariablesPrefixSize > 0 -> allTypeVariables.keys.take(outerSystemVariablesPrefixSize).toSet()
+                    else -> null
+                }
+
+        /**
+         * If not null, that property means that we should assume temporary them all as proper types when fixating some variables.
          *
          * By default, if that property is null, we assume all `allTypeVariables` as not proper.
          *
          * Currently, that is only used for `provideDelegate` resolution, see
          * [org.jetbrains.kotlin.fir.resolve.transformers.body.resolve.FirDeclarationsResolveTransformer.fixInnerVariablesForProvideDelegateIfNeeded]
          */
-        val typeVariablesThatAreNotCountedAsProperTypes: Set<TypeConstructorMarker>?
+        val typeVariablesThatAreCountedAsProperTypes: Set<TypeConstructorMarker>?
 
         fun isReified(variable: TypeVariableMarker): Boolean
     }
 
-    data class VariableForFixation(
+    class VariableForFixation(
         val variable: TypeConstructorMarker,
-        val hasProperConstraint: Boolean,
-    )
+        private val hasProperConstraint: Boolean,
+        private val hasDependencyOnOuterTypeVariable: Boolean = false,
+    ) {
+        val isReady: Boolean get() = hasProperConstraint && !hasDependencyOnOuterTypeVariable
+    }
 
     fun findFirstVariableForFixation(
         c: Context,
@@ -53,11 +68,13 @@
         postponedKtPrimitives: List<PostponedResolvedAtomMarker>,
         completionMode: ConstraintSystemCompletionMode,
         topLevelType: KotlinTypeMarker,
-    ): VariableForFixation? = c.findTypeVariableForFixation(allTypeVariables, postponedKtPrimitives, completionMode, topLevelType)
+    ): VariableForFixation? =
+        c.findTypeVariableForFixation(allTypeVariables, postponedKtPrimitives, completionMode, topLevelType)
 
     enum class TypeVariableFixationReadiness {
         FORBIDDEN,
         WITHOUT_PROPER_ARGUMENT_CONSTRAINT, // proper constraint from arguments -- not from upper bound for type parameters
+        OUTER_TYPE_VARIABLE_DEPENDENCY,
         READY_FOR_FIXATION_DECLARED_UPPER_BOUND_WITH_SELF_TYPES,
         WITH_COMPLEX_DEPENDENCY, // if type variable T has constraint with non fixed type variable inside (non-top-level): T <: Foo<S>
         ALL_CONSTRAINTS_TRIVIAL_OR_NON_PROPER, // proper trivial constraint from arguments, Nothing <: T
@@ -85,6 +102,7 @@
         isTypeInferenceForSelfTypesSupported && areAllProperConstraintsSelfTypeBased(variable) ->
             TypeVariableFixationReadiness.READY_FOR_FIXATION_DECLARED_UPPER_BOUND_WITH_SELF_TYPES
         !variableHasProperArgumentConstraints(variable) -> TypeVariableFixationReadiness.WITHOUT_PROPER_ARGUMENT_CONSTRAINT
+        dependencyProvider.isRelatedToOuterTypeVariable(variable) -> TypeVariableFixationReadiness.OUTER_TYPE_VARIABLE_DEPENDENCY
         hasDependencyToOtherTypeVariables(variable) -> TypeVariableFixationReadiness.WITH_COMPLEX_DEPENDENCY
         // TODO: Consider removing this kind of readiness, see KT-63032
         allConstraintsTrivialOrNonProper(variable) -> TypeVariableFixationReadiness.ALL_CONSTRAINTS_TRIVIAL_OR_NON_PROPER
@@ -152,7 +170,7 @@
         if (allTypeVariables.isEmpty()) return null
 
         val dependencyProvider = TypeVariableDependencyInformationProvider(
-            notFixedTypeVariables, postponedArguments, topLevelType.takeIf { completionMode == PARTIAL }, this
+            notFixedTypeVariables, postponedArguments, topLevelType.takeIf { completionMode == PARTIAL }, this,
         )
 
         val candidate =
@@ -161,6 +179,8 @@
         return when (getTypeVariableReadiness(candidate, dependencyProvider)) {
             TypeVariableFixationReadiness.FORBIDDEN -> null
             TypeVariableFixationReadiness.WITHOUT_PROPER_ARGUMENT_CONSTRAINT -> VariableForFixation(candidate, false)
+            TypeVariableFixationReadiness.OUTER_TYPE_VARIABLE_DEPENDENCY ->
+                VariableForFixation(candidate, hasProperConstraint = true, hasDependencyOnOuterTypeVariable = true)
 
             else -> VariableForFixation(candidate, true)
         }
@@ -190,12 +210,13 @@
                 && !c.isNullabilityConstraint
 
     private fun Context.isProperType(type: KotlinTypeMarker): Boolean =
-        isProperTypeForFixation(type) { t -> !t.contains { isNotFixedRelevantVariable(it) } }
+        isProperTypeForFixation(type, notFixedTypeVariables.keys) { t -> !t.contains { isNotFixedRelevantVariable(it) } }
 
     private fun Context.isNotFixedRelevantVariable(it: KotlinTypeMarker): Boolean {
-        if (!notFixedTypeVariables.containsKey(it.typeConstructor())) return false
-        if (typeVariablesThatAreNotCountedAsProperTypes == null) return true
-        return typeVariablesThatAreNotCountedAsProperTypes!!.contains(it.typeConstructor())
+        val key = it.typeConstructor()
+        if (!notFixedTypeVariables.containsKey(key)) return false
+        if (typeVariablesThatAreCountedAsProperTypes?.contains(key) == true) return false
+        return true
     }
 
     private fun Context.isReified(variable: TypeConstructorMarker): Boolean =
@@ -235,8 +256,25 @@
     }
 }
 
-inline fun TypeSystemInferenceExtensionContext.isProperTypeForFixation(type: KotlinTypeMarker, isProper: (KotlinTypeMarker) -> Boolean) =
-    isProper(type) && extractProjectionsForAllCapturedTypes(type).all(isProper)
+/**
+ * Returns `false` for fixed type variables types even if `isProper(type) == true`
+ * Thus allowing only non-TVs types to be used for fixation on top level.
+ * While this limitation is important, it doesn't really limit final results because when we have a constraint like T <: E or E <: T
+ * and we're going to fix T into E, we assume that if E has some other constraints, they are being incorporated to T, so we would choose
+ * them instead of E itself.
+ */
+inline fun TypeSystemInferenceExtensionContext.isProperTypeForFixation(
+    type: KotlinTypeMarker,
+    notFixedTypeVariables: Set<TypeConstructorMarker>,
+    isProper: (KotlinTypeMarker) -> Boolean
+): Boolean {
+    // We don't allow fixing T into any top-level TV type, like T := F or T := F & Any
+    // Even if F is considered as a proper by `isProper` (e.g., it belongs to an outer CS)
+    // But at the same time, we don't forbid fixing into T := MutableList<F>
+    if (type.typeConstructor() in notFixedTypeVariables) return false
+
+    return isProper(type) && extractProjectionsForAllCapturedTypes(type).all(isProper)
+}
 
 fun TypeSystemInferenceExtensionContext.extractProjectionsForAllCapturedTypes(baseType: KotlinTypeMarker): Set<KotlinTypeMarker> {
     if (baseType.isFlexible()) {
diff --git a/compiler/resolution.common/src/org/jetbrains/kotlin/resolve/calls/inference/model/ConstraintStorage.kt b/compiler/resolution.common/src/org/jetbrains/kotlin/resolve/calls/inference/model/ConstraintStorage.kt
index 9f8fb0f..40d87ec 100644
--- a/compiler/resolution.common/src/org/jetbrains/kotlin/resolve/calls/inference/model/ConstraintStorage.kt
+++ b/compiler/resolution.common/src/org/jetbrains/kotlin/resolve/calls/inference/model/ConstraintStorage.kt
@@ -49,6 +49,8 @@
     val constraintsFromAllForkPoints: List<Pair<IncorporationConstraintPosition, ForkPointData>>
 
     /**
+     *  Outer system for a call means some set of variables defined beside it/its arguments
+     *
      *  In case some candidate's CS is built in the context of some outer CS, first [outerSystemVariablesPrefixSize] in the list
      *  of [allTypeVariables] belong to the outer CS.
      *
@@ -62,6 +64,8 @@
      */
     val outerSystemVariablesPrefixSize: Int
 
+    val usesOuterCs: Boolean
+
     object Empty : ConstraintStorage {
         override val allTypeVariables: Map<TypeConstructorMarker, TypeVariableMarker> get() = emptyMap()
         override val notFixedTypeVariables: Map<TypeConstructorMarker, VariableWithConstraints> get() = emptyMap()
@@ -77,6 +81,8 @@
         override val constraintsFromAllForkPoints: List<Pair<IncorporationConstraintPosition, ForkPointData>> = emptyList()
 
         override val outerSystemVariablesPrefixSize: Int get() = 0
+
+        override val usesOuterCs: Boolean get() = false
     }
 }
 
diff --git a/compiler/resolution.common/src/org/jetbrains/kotlin/resolve/calls/inference/model/MutableConstraintStorage.kt b/compiler/resolution.common/src/org/jetbrains/kotlin/resolve/calls/inference/model/MutableConstraintStorage.kt
index 700ad4e..9f66363 100644
--- a/compiler/resolution.common/src/org/jetbrains/kotlin/resolve/calls/inference/model/MutableConstraintStorage.kt
+++ b/compiler/resolution.common/src/org/jetbrains/kotlin/resolve/calls/inference/model/MutableConstraintStorage.kt
@@ -221,6 +221,14 @@
         }
     }
 
+    fun runConstraintsSimplification() {
+        val currentState = constraints.toList()
+        mutableConstraints.apply {
+            clear()
+            addAll(currentState)
+        }
+    }
+
     private fun isUsefulConstraint(constraint: Constraint, equalityConstraints: Map<Int, List<Constraint>>): Boolean {
         if (constraint.kind == ConstraintKind.EQUALITY) return true
         return equalityConstraints[constraint.typeHashCode]?.none { it.type == constraint.type } ?: true
@@ -251,4 +259,12 @@
     override val constraintsFromAllForkPoints: MutableList<Pair<IncorporationConstraintPosition, ForkPointData>> = SmartList()
 
     override var outerSystemVariablesPrefixSize: Int = 0
+
+    override var usesOuterCs: Boolean = false
+
+    @AssertionsOnly
+    internal var outerCS: ConstraintStorage? = null
 }
+
+@RequiresOptIn
+annotation class AssertionsOnly
\ No newline at end of file
diff --git a/compiler/resolution.common/src/org/jetbrains/kotlin/resolve/calls/inference/model/NewConstraintSystemImpl.kt b/compiler/resolution.common/src/org/jetbrains/kotlin/resolve/calls/inference/model/NewConstraintSystemImpl.kt
index d14628c..dbbc6da 100644
--- a/compiler/resolution.common/src/org/jetbrains/kotlin/resolve/calls/inference/model/NewConstraintSystemImpl.kt
+++ b/compiler/resolution.common/src/org/jetbrains/kotlin/resolve/calls/inference/model/NewConstraintSystemImpl.kt
@@ -41,7 +41,7 @@
     private val properTypesCache: MutableSet<KotlinTypeMarker> = SmartSet.create()
     private val notProperTypesCache: MutableSet<KotlinTypeMarker> = SmartSet.create()
     private val intersectionTypesCache: MutableMap<Collection<KotlinTypeMarker>, EmptyIntersectionTypeInfo?> = mutableMapOf()
-    override var typeVariablesThatAreNotCountedAsProperTypes: Set<TypeConstructorMarker>? = null
+    override var typeVariablesThatAreCountedAsProperTypes: Set<TypeConstructorMarker>? = null
 
     private var couldBeResolvedWithUnrestrictedBuilderInference: Boolean = false
 
@@ -51,23 +51,26 @@
      * @see [org.jetbrains.kotlin.resolve.calls.inference.components.VariableFixationFinder.Context.typeVariablesThatAreNotCountedAsProperTypes]
      * @see [org.jetbrains.kotlin.fir.resolve.transformers.body.resolve.FirDeclarationsResolveTransformer.fixInnerVariablesForProvideDelegateIfNeeded]
      */
-    fun withTypeVariablesThatAreNotCountedAsProperTypes(typeVariables: Set<TypeConstructorMarker>, block: () -> Unit) {
+    @K2Only
+    override fun <R> withTypeVariablesThatAreCountedAsProperTypes(typeVariables: Set<TypeConstructorMarker>, block: () -> R): R {
         checkState(State.BUILDING)
         // Cleaning cache is necessary because temporarily we change the meaning of what does "proper type" mean
         properTypesCache.clear()
         notProperTypesCache.clear()
 
-        require(typeVariablesThatAreNotCountedAsProperTypes == null) {
+        require(typeVariablesThatAreCountedAsProperTypes == null) {
             "Currently there should be no nested withDisallowingOnlyThisTypeVariablesForProperTypes calls"
         }
 
-        typeVariablesThatAreNotCountedAsProperTypes = typeVariables
+        typeVariablesThatAreCountedAsProperTypes = typeVariables
 
-        block()
+        val result = block()
 
-        typeVariablesThatAreNotCountedAsProperTypes = null
+        typeVariablesThatAreCountedAsProperTypes = null
         properTypesCache.clear()
         notProperTypesCache.clear()
+
+        return result
     }
 
     private enum class State {
@@ -221,6 +224,7 @@
     // ConstraintSystemBuilder
     private fun transactionRegisterVariable(variable: TypeVariableMarker) {
         if (state != State.TRANSACTION) return
+        if (variable.freshTypeConstructor() in storage.allTypeVariables) return
         typeVariablesTransaction.add(variable)
     }
 
@@ -302,13 +306,25 @@
         }
 
     fun addOuterSystem(outerSystem: ConstraintStorage) {
-        addOtherSystem(outerSystem)
+        require(!storage.usesOuterCs)
+
+        storage.usesOuterCs = true
         storage.outerSystemVariablesPrefixSize = outerSystem.allTypeVariables.size
+        @OptIn(AssertionsOnly::class)
+        storage.outerCS = outerSystem
+
+        addOtherSystem(outerSystem, isAddingOuter = true)
     }
 
-    fun setBaseSystem(outerSystem: ConstraintStorage) {
-        addOtherSystem(outerSystem)
-        storage.outerSystemVariablesPrefixSize = outerSystem.outerSystemVariablesPrefixSize
+    @K2Only
+    fun setBaseSystem(baseSystem: ConstraintStorage) {
+        require(storage.allTypeVariables.isEmpty())
+        storage.usesOuterCs = baseSystem.usesOuterCs
+        storage.outerSystemVariablesPrefixSize = baseSystem.outerSystemVariablesPrefixSize
+        @OptIn(AssertionsOnly::class)
+        storage.outerCS = (baseSystem as? MutableConstraintStorage)?.outerCS
+
+        addOtherSystem(baseSystem)
     }
 
     fun prepareForGlobalCompletion() {
@@ -317,6 +333,16 @@
     }
 
     override fun addOtherSystem(otherSystem: ConstraintStorage) {
+        addOtherSystem(otherSystem, isAddingOuter = false)
+    }
+
+    fun replaceContentWith(otherSystem: ConstraintStorage) {
+        addOtherSystem(otherSystem, isAddingOuter = false, clearNotFixedTypeVariables = true)
+    }
+
+    private fun addOtherSystem(otherSystem: ConstraintStorage, isAddingOuter: Boolean, clearNotFixedTypeVariables: Boolean = false) {
+        runOuterCSRelatedAssertions(otherSystem, isAddingOuter)
+
         if (otherSystem.allTypeVariables.isNotEmpty()) {
             otherSystem.allTypeVariables.forEach {
                 transactionRegisterVariable(it.value)
@@ -324,16 +350,47 @@
             storage.allTypeVariables.putAll(otherSystem.allTypeVariables)
             notProperTypesCache.clear()
         }
+
+        // `clearNotFixedTypeVariables` means that we're mostly replacing the content, thus we need to remove variables that have been fixed
+        // in `otherSystem` from `this.notFixedTypeVariables`, too
+        if (clearNotFixedTypeVariables) {
+            notFixedTypeVariables.clear()
+        }
+
         for ((variable, constraints) in otherSystem.notFixedTypeVariables) {
             notFixedTypeVariables[variable] = MutableVariableWithConstraints(this, constraints)
         }
-        storage.initialConstraints.addAll(otherSystem.initialConstraints)
+
+        val currentInitialConstraints = storage.initialConstraints.toSet()
+
+        otherSystem.initialConstraints.filterTo(storage.initialConstraints) {
+            it !in currentInitialConstraints
+        }
+
         storage.maxTypeDepthFromInitialConstraints =
             max(storage.maxTypeDepthFromInitialConstraints, otherSystem.maxTypeDepthFromInitialConstraints)
         storage.errors.addAll(otherSystem.errors)
         storage.fixedTypeVariables.putAll(otherSystem.fixedTypeVariables)
         storage.postponedTypeVariables.addAll(otherSystem.postponedTypeVariables)
         storage.constraintsFromAllForkPoints.addAll(otherSystem.constraintsFromAllForkPoints)
+
+    }
+
+
+    private fun runOuterCSRelatedAssertions(otherSystem: ConstraintStorage, isAddingOuter: Boolean) {
+        if (!otherSystem.usesOuterCs) return
+
+        // When integrating a child system back, it's ok that for root CS, `storage.usesOuterCs == false`
+        @OptIn(AssertionsOnly::class)
+        if ((otherSystem as? MutableConstraintStorage)?.outerCS === storage) return
+
+        require(storage.usesOuterCs)
+
+        if (!isAddingOuter) {
+            require(storage.outerSystemVariablesPrefixSize == otherSystem.outerSystemVariablesPrefixSize) {
+                "Expected to be ${otherSystem.outerSystemVariablesPrefixSize}, but ${storage.outerSystemVariablesPrefixSize} found"
+            }
+        }
     }
 
     // ResultTypeResolver.Context, ConstraintSystemBuilder
@@ -357,11 +414,11 @@
                 it
 
             if (typeToCheck == null) return@contains false
-            if (typeVariablesThatAreNotCountedAsProperTypes != null) {
-                return@contains typeVariablesThatAreNotCountedAsProperTypes!!.contains(typeToCheck.typeConstructor())
+            if (typeVariablesThatAreCountedAsProperTypes?.contains(typeToCheck.typeConstructor()) == true) {
+                return@contains false
             }
 
-            storage.allTypeVariables.containsKey(typeToCheck.typeConstructor())
+            return@contains storage.allTypeVariables.containsKey(typeToCheck.typeConstructor())
         }
 
     override fun isTypeVariable(type: KotlinTypeMarker): Boolean {
@@ -530,6 +587,13 @@
             otherVariableWithConstraints.removeConstrains { containsTypeVariable(it.type, freshTypeConstructor) }
         }
 
+        val substitutorForFixedVariables = typeSubstitutorByTypeConstructor(mapOf(freshTypeConstructor to resultType))
+
+        storage.fixedTypeVariables.filter { containsTypeVariable(it.value, freshTypeConstructor) }
+            .forEach { (otherVariable, otherResultType) ->
+                storage.fixedTypeVariables[otherVariable] = substitutorForFixedVariables.safeSubstitute(otherResultType)
+            }
+
         storage.fixedTypeVariables[freshTypeConstructor] = resultType
 
         // Substitute freshly fixed type variable into missed constraints
@@ -689,7 +753,7 @@
         return buildCurrentSubstitutor(emptyMap())
     }
 
-    override fun buildCurrentSubstitutor(additionalBindings: Map<TypeConstructorMarker, StubTypeMarker>): TypeSubstitutorMarker {
+    override fun buildCurrentSubstitutor(additionalBindings: Map<TypeConstructorMarker, KotlinTypeMarker>): TypeSubstitutorMarker {
         checkState(State.BUILDING, State.COMPLETION, State.TRANSACTION)
         return storage.buildCurrentSubstitutor(this, additionalBindings)
     }
@@ -715,6 +779,8 @@
         return storage
     }
 
+    val usesOuterCs: Boolean get() = storage.usesOuterCs
+
     // PostponedArgumentsAnalyzer.Context
     override fun hasUpperOrEqualUnitConstraint(type: KotlinTypeMarker): Boolean {
         checkState(State.BUILDING, State.COMPLETION, State.FREEZED)
diff --git a/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/components/KotlinCallCompleter.kt b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/components/KotlinCallCompleter.kt
index 45740d7..6f2688b 100644
--- a/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/components/KotlinCallCompleter.kt
+++ b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/components/KotlinCallCompleter.kt
@@ -10,22 +10,25 @@
 import org.jetbrains.kotlin.config.LanguageVersionSettings
 import org.jetbrains.kotlin.descriptors.FunctionDescriptor
 import org.jetbrains.kotlin.descriptors.synthetic.SyntheticMemberDescriptor
-import org.jetbrains.kotlin.resolve.calls.components.candidate.ResolutionCandidate
 import org.jetbrains.kotlin.resolve.calls.components.candidate.CallableReferenceResolutionCandidate
+import org.jetbrains.kotlin.resolve.calls.components.candidate.ResolutionCandidate
 import org.jetbrains.kotlin.resolve.calls.components.candidate.SimpleResolutionCandidate
 import org.jetbrains.kotlin.resolve.calls.inference.NewConstraintSystem
 import org.jetbrains.kotlin.resolve.calls.inference.addEqualityConstraintIfCompatible
-import org.jetbrains.kotlin.resolve.calls.inference.components.*
+import org.jetbrains.kotlin.resolve.calls.inference.components.ConstraintSystemCompletionMode
+import org.jetbrains.kotlin.resolve.calls.inference.components.KotlinConstraintSystemCompleter
+import org.jetbrains.kotlin.resolve.calls.inference.components.NewTypeSubstitutorByConstructorMap
+import org.jetbrains.kotlin.resolve.calls.inference.components.TrivialConstraintTypeInferenceOracle
 import org.jetbrains.kotlin.resolve.calls.inference.model.ConstraintStorage.Empty.hasContradiction
 import org.jetbrains.kotlin.resolve.calls.inference.model.ExpectedTypeConstraintPositionImpl
 import org.jetbrains.kotlin.resolve.calls.model.*
 import org.jetbrains.kotlin.resolve.calls.tower.CandidateFactory
 import org.jetbrains.kotlin.resolve.calls.tower.forceResolution
-import org.jetbrains.kotlin.types.error.ErrorUtils
 import org.jetbrains.kotlin.types.TypeUtils
 import org.jetbrains.kotlin.types.UnwrappedType
 import org.jetbrains.kotlin.types.error.ErrorType
 import org.jetbrains.kotlin.types.error.ErrorTypeKind
+import org.jetbrains.kotlin.types.error.ErrorUtils
 import org.jetbrains.kotlin.types.model.safeSubstitute
 import org.jetbrains.kotlin.utils.addToStdlib.same
 
@@ -80,6 +83,7 @@
                 candidate.runCompletion(completionMode, diagnosticHolder, resolutionCallbacks)
                 candidate.asCallResolutionResult(completionMode, diagnosticHolder)
             }
+            ConstraintSystemCompletionMode.PCLA_POSTPONED_CALL -> error("PCLA might be only run for K2")
             ConstraintSystemCompletionMode.UNTIL_FIRST_LAMBDA -> throw IllegalStateException("Should not be here")
 
         }
diff --git a/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/inference/components/KotlinConstraintSystemCompleter.kt b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/inference/components/KotlinConstraintSystemCompleter.kt
index 9585b46..f5dbfd2 100644
--- a/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/inference/components/KotlinConstraintSystemCompleter.kt
+++ b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/inference/components/KotlinConstraintSystemCompleter.kt
@@ -12,7 +12,10 @@
 import org.jetbrains.kotlin.resolve.calls.inference.model.*
 import org.jetbrains.kotlin.resolve.calls.model.*
 import org.jetbrains.kotlin.resolve.calls.model.MultiLambdaBuilderInferenceRestriction
-import org.jetbrains.kotlin.types.*
+import org.jetbrains.kotlin.types.KotlinType
+import org.jetbrains.kotlin.types.KotlinTypeFactory
+import org.jetbrains.kotlin.types.TypeConstructor
+import org.jetbrains.kotlin.types.UnwrappedType
 import org.jetbrains.kotlin.types.error.ErrorTypeKind
 import org.jetbrains.kotlin.types.error.ErrorUtils
 import org.jetbrains.kotlin.types.model.*
@@ -279,7 +282,7 @@
         // continue completion (rerun stages) only if ready for fixation variables with proper constraints have appeared
         // (after analysing a lambda with the builder inference)
         // otherwise we don't continue and report "not enough type information" error
-        return variableForFixation?.hasProperConstraint == true
+        return variableForFixation?.isReady == true
     }
 
     private fun transformToAtomWithNewFunctionalExpectedType(
@@ -319,7 +322,7 @@
             topLevelType
         ) ?: return false
 
-        if (!variableForFixation.hasProperConstraint) return false
+        if (!variableForFixation.isReady) return false
 
         fixVariable(this, notFixedTypeVariables.getValue(variableForFixation.variable), topLevelAtoms, diagnosticsHolder)
 
@@ -340,7 +343,7 @@
                 postponedArguments, completionMode, topLevelType,
             ) ?: break
 
-            assert(!variableForFixation.hasProperConstraint) {
+            assert(!variableForFixation.isReady) {
                 "At this stage there should be no remaining variables with proper constraints"
             }
 
diff --git a/core/compiler.common/src/org/jetbrains/kotlin/types/model/TypeSystemContext.kt b/core/compiler.common/src/org/jetbrains/kotlin/types/model/TypeSystemContext.kt
index 7d69e04..4ed5080 100644
--- a/core/compiler.common/src/org/jetbrains/kotlin/types/model/TypeSystemContext.kt
+++ b/core/compiler.common/src/org/jetbrains/kotlin/types/model/TypeSystemContext.kt
@@ -8,7 +8,9 @@
 import org.jetbrains.kotlin.builtins.functions.FunctionTypeKind
 import org.jetbrains.kotlin.resolve.checkers.EmptyIntersectionTypeChecker
 import org.jetbrains.kotlin.resolve.checkers.EmptyIntersectionTypeInfo
-import org.jetbrains.kotlin.types.*
+import org.jetbrains.kotlin.types.AbstractTypeChecker
+import org.jetbrains.kotlin.types.TypeCheckerState
+import org.jetbrains.kotlin.types.Variance
 import kotlin.contracts.ExperimentalContracts
 import kotlin.contracts.contract
 
@@ -551,6 +553,9 @@
     fun typeSubstitutorByTypeConstructor(map: Map<TypeConstructorMarker, KotlinTypeMarker>): TypeSubstitutorMarker
     fun createEmptySubstitutor(): TypeSubstitutorMarker
 
+    @K2Only
+    fun createSubstitutionFromSubtypingStubTypesToTypeVariables(): TypeSubstitutorMarker
+
     /**
      * @returns substituted type or [type] if there were no substitution
      */
@@ -588,3 +593,6 @@
 
 @RequiresOptIn("This kinds of type is obsolete and should not be used until you really need it")
 annotation class ObsoleteTypeKind
+
+@RequiresOptIn
+annotation class K2Only
diff --git a/core/descriptors/src/org/jetbrains/kotlin/types/checker/ClassicTypeSystemContext.kt b/core/descriptors/src/org/jetbrains/kotlin/types/checker/ClassicTypeSystemContext.kt
index 569c1a9..e755500 100644
--- a/core/descriptors/src/org/jetbrains/kotlin/types/checker/ClassicTypeSystemContext.kt
+++ b/core/descriptors/src/org/jetbrains/kotlin/types/checker/ClassicTypeSystemContext.kt
@@ -622,6 +622,11 @@
         errorSupportedOnlyInTypeInference()
     }
 
+    @K2Only
+    override fun createSubstitutionFromSubtypingStubTypesToTypeVariables(): TypeSubstitutorMarker {
+        error("Only for K2")
+    }
+
     override fun TypeSubstitutorMarker.safeSubstitute(type: KotlinTypeMarker): KotlinTypeMarker {
         require(type is UnwrappedType, type::errorMessage)
         require(this is TypeSubstitutor, this::errorMessage)