[Lower] Extract lifting logic from `LocalDeclarationsInInlineLambdasPreparationLowering`

^KT-78856
diff --git a/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/inline/LocalDeclarationsInInlineLambdas.kt b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/inline/LocalDeclarationsInInlineLambdas.kt
index 7f47e8a..71f8da0 100644
--- a/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/inline/LocalDeclarationsInInlineLambdas.kt
+++ b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/inline/LocalDeclarationsInInlineLambdas.kt
@@ -11,7 +11,6 @@
 import org.jetbrains.kotlin.backend.common.lower.LocalDeclarationsLowering
 import org.jetbrains.kotlin.backend.common.lower.VisibilityPolicy
 import org.jetbrains.kotlin.backend.common.phaser.PhaseDescription
-import org.jetbrains.kotlin.backend.common.runOnFilePostfix
 import org.jetbrains.kotlin.descriptors.DescriptorVisibility
 import org.jetbrains.kotlin.ir.IrElement
 import org.jetbrains.kotlin.ir.IrStatement
@@ -22,6 +21,7 @@
 import org.jetbrains.kotlin.ir.transformStatement
 import org.jetbrains.kotlin.ir.util.isInlineParameter
 import org.jetbrains.kotlin.ir.util.isOriginallyLocalDeclaration
+import org.jetbrains.kotlin.ir.util.isSyntheticBlockForInlineCall
 import org.jetbrains.kotlin.ir.util.setDeclarationsParent
 import org.jetbrains.kotlin.ir.visitors.*
 
@@ -39,10 +39,6 @@
  */
 @PhaseDescription("LocalDeclarationsInInlineLambdasPreparationLowering")
 class LocalDeclarationsInInlineLambdasPreparationLowering(val context: LoweringContext) : BodyLoweringPass {
-    override fun lower(irFile: IrFile) {
-        runOnFilePostfix(irFile)
-    }
-
     override fun lower(irBody: IrBody, container: IrDeclaration) {
         irBody.transformChildren(object : IrTransformer<IrDeclarationParent>() {
             override fun visitDeclaration(declaration: IrDeclarationBase, data: IrDeclarationParent) =
@@ -53,20 +49,7 @@
                 if (!rootCallee.isInline)
                     return super.visitCall(expression, data)
 
-                val inlineLambdas = mutableListOf<IrFunction>()
-                for (index in expression.arguments.indices) {
-                    val argument = expression.arguments[index]
-                    val inlineLambda = when (argument) {
-                        is IrRichPropertyReference -> argument.getterFunction
-                        is IrRichFunctionReference -> argument.invokeFunction
-                        else -> null
-                    }?.takeIf { rootCallee.parameters[index].isInlineParameter() }
-                    if (inlineLambda == null)
-                        expression.arguments[index] = argument?.transform(this, data)
-                    else
-                        inlineLambdas.add(inlineLambda)
-                }
-
+                val inlineLambdas = collectInlineLambdas(expression, rootCallee, this, data)
                 if (inlineLambdas.isEmpty())
                     return expression
 
@@ -102,8 +85,10 @@
                 // TODO: Remove fragment above after fixing KT-77103
 
                 val irBlock = IrBlockImpl(expression.startOffset, expression.endOffset, expression.type).apply {
+                    isSyntheticBlockForInlineCall = true
                     statements += expression
                 }
+
                 LocalDeclarationsLowering(
                     context,
                     visibilityPolicy = object : VisibilityPolicy {
@@ -122,11 +107,53 @@
                     remapCapturedTypesInExtractedLocalDeclarations = false,
                 ).lower(irBlock, container, data)
 
-                val localDeclarationsToPopUp = mutableListOf<IrDeclaration>()
-
                 val outerTransformer = this
                 for (lambda in inlineLambdas) {
-                    lambda.transformChildrenVoid(object : IrElementTransformerVoid() {
+                    lambda.transformChildrenVoid(object : LocalDeclarationsInInlineLambdasTransformer() {
+                        override fun visitSimpleFunctionOrClass(declaration: IrDeclaration): IrStatement {
+                            // Recursive call to outer transformer for handling nested inline lambdas
+                            declaration.transformChildren(outerTransformer, declaration as IrDeclarationParent)
+                            return declaration
+                        }
+                    })
+                }
+
+                return irBlock
+            }
+        }, container as? IrDeclarationParent ?: container.parent)
+    }
+}
+
+@PhaseDescription("LocalDeclarationsInInlineLambdasPopupLowering")
+class LocalDeclarationsInInlineLambdasPopupLowering(val context: LoweringContext) : BodyLoweringPass {
+    override fun lower(irBody: IrBody, container: IrDeclaration) {
+        irBody.transformChildren(object : IrTransformer<IrDeclarationParent>() {
+            override fun visitDeclaration(declaration: IrDeclarationBase, data: IrDeclarationParent) =
+                super.visitDeclaration(declaration, (declaration as? IrDeclarationParent) ?: data)
+
+            override fun visitBlock(expression: IrBlock, data: IrDeclarationParent): IrExpression {
+                if (!expression.isSyntheticBlockForInlineCall) return super.visitBlock(expression, data)
+
+                val call = expression.statements.firstOrNull() as? IrCall ?: return expression
+                val rootCallee = call.symbol.owner
+                val inlineLambdas = collectInlineLambdas(call, rootCallee, this, data)
+
+                val localDeclarationsToPopUp = mutableListOf<IrDeclaration>()
+                val outerTransformer = this
+                for (lambda in inlineLambdas) {
+                    lambda.transformChildrenVoid(object : LocalDeclarationsInInlineLambdasTransformer() {
+                        override fun visitSimpleFunctionOrClass(declaration: IrDeclaration): IrStatement {
+                            // Recursive call to outer transformer for handling nested inline lambdas
+                            declaration.transformChildren(outerTransformer, declaration as IrDeclarationParent)
+                            return if (declaration.isOriginallyLocalDeclaration) {
+                                localDeclarationsToPopUp += declaration
+                                IrCompositeImpl(
+                                    declaration.startOffset, declaration.endOffset,
+                                    context.irBuiltIns.unitType
+                                )
+                            } else declaration
+                        }
+
                         override fun visitLocalDelegatedProperty(declaration: IrLocalDelegatedProperty): IrStatement {
                             declaration.getter.transformStatement(this)
                             declaration.setter?.transformStatement(this)
@@ -145,32 +172,45 @@
                             expression.setterFunction?.transformChildrenVoid(this)
                             return expression
                         }
-
-                        override fun visitClass(declaration: IrClass): IrStatement = visitSimpleFunctionOrClass(declaration)
-
-                        override fun visitSimpleFunction(declaration: IrSimpleFunction): IrStatement =
-                            visitSimpleFunctionOrClass(declaration)
-
-                        private fun visitSimpleFunctionOrClass(declaration: IrDeclaration): IrStatement {
-                            // Recursive call to outer transformer for handling nested inline lambdas
-                            declaration.transformChildren(outerTransformer, declaration as IrDeclarationParent)
-                            return if (declaration.isOriginallyLocalDeclaration) {
-                                localDeclarationsToPopUp += declaration
-                                IrCompositeImpl(
-                                    declaration.startOffset, declaration.endOffset,
-                                    context.irBuiltIns.unitType
-                                )
-                            } else declaration
-                        }
-
                     })
                 }
 
-                irBlock.statements.addAll(0, localDeclarationsToPopUp)
+                expression.statements.addAll(0, localDeclarationsToPopUp)
                 localDeclarationsToPopUp.forEach { it.setDeclarationsParent(data) }
 
-                return irBlock
+                return expression
             }
         }, container as? IrDeclarationParent ?: container.parent)
     }
 }
+
+private abstract class LocalDeclarationsInInlineLambdasTransformer() : IrElementTransformerVoid() {
+    abstract fun visitSimpleFunctionOrClass(declaration: IrDeclaration): IrStatement
+
+    override fun visitSimpleFunction(declaration: IrSimpleFunction): IrStatement = visitSimpleFunctionOrClass(declaration)
+
+    override fun visitClass(declaration: IrClass): IrStatement = visitSimpleFunctionOrClass(declaration)
+}
+
+private fun collectInlineLambdas(
+    expression: IrCall,
+    rootCallee: IrSimpleFunction,
+    transformer: IrTransformer<IrDeclarationParent>,
+    data: IrDeclarationParent,
+): MutableList<IrFunction> {
+    val inlineLambdas = mutableListOf<IrFunction>()
+    for (index in expression.arguments.indices) {
+        val argument = expression.arguments[index]
+        val inlineLambda = when (argument) {
+            is IrRichPropertyReference -> argument.getterFunction
+            is IrRichFunctionReference -> argument.invokeFunction
+            else -> null
+        }?.takeIf { rootCallee.parameters[index].isInlineParameter() }
+        if (inlineLambda == null) {
+            expression.arguments[index] = argument?.transform(transformer, data)
+        } else
+            inlineLambdas.add(inlineLambda)
+    }
+
+    return inlineLambdas
+}
diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/JsLoweringPhases.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/JsLoweringPhases.kt
index 6a690a6..b5790a2 100644
--- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/JsLoweringPhases.kt
+++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/JsLoweringPhases.kt
@@ -10,6 +10,7 @@
 import org.jetbrains.kotlin.backend.common.lower.*
 import org.jetbrains.kotlin.backend.common.lower.coroutines.AddContinuationToLocalSuspendFunctionsLowering
 import org.jetbrains.kotlin.backend.common.lower.coroutines.AddContinuationToNonLocalSuspendFunctionsLowering
+import org.jetbrains.kotlin.backend.common.lower.inline.LocalDeclarationsInInlineLambdasPopupLowering
 import org.jetbrains.kotlin.backend.common.lower.inline.LocalDeclarationsInInlineLambdasPreparationLowering
 import org.jetbrains.kotlin.backend.common.lower.loops.ForLoopsLowering
 import org.jetbrains.kotlin.backend.common.phaser.*
@@ -180,6 +181,12 @@
     name = "LocalDeclarationsInInlineLambdasPreparationPhase",
 )
 
+private val popupLocalDeclarationsInInlineLambdasPhase = makeIrModulePhase(
+    ::LocalDeclarationsInInlineLambdasPopupLowering,
+    name = "LocalDeclarationsInInlineLambdasPopupLowering",
+    prerequisite = setOf(prepareLocalDeclarationsInInlineLambdasPhase)
+)
+
 private val replaceSuspendIntrinsicLowering = makeIrModulePhase(
     ::ReplaceSuspendIntrinsicLowering,
     name = "ReplaceSuspendIntrinsicLowering",
@@ -739,6 +746,7 @@
     lateinitPhase,
     sharedVariablesLoweringPhase,
     prepareLocalDeclarationsInInlineLambdasPhase,
+    popupLocalDeclarationsInInlineLambdasPhase,
     arrayConstructorPhase,
     inlineOnlyPrivateFunctionsPhase,
     outerThisSpecialAccessorInInlineFunctionsPhase,
diff --git a/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/WasmLoweringPhases.kt b/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/WasmLoweringPhases.kt
index 4d94881..7d90b0f 100644
--- a/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/WasmLoweringPhases.kt
+++ b/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/WasmLoweringPhases.kt
@@ -9,6 +9,7 @@
 import org.jetbrains.kotlin.backend.common.ir.Symbols
 import org.jetbrains.kotlin.backend.common.lower.*
 import org.jetbrains.kotlin.backend.common.lower.coroutines.AddContinuationToNonLocalSuspendFunctionsLowering
+import org.jetbrains.kotlin.backend.common.lower.inline.LocalDeclarationsInInlineLambdasPopupLowering
 import org.jetbrains.kotlin.backend.common.lower.inline.LocalDeclarationsInInlineLambdasPreparationLowering
 import org.jetbrains.kotlin.backend.common.lower.loops.ForLoopsLowering
 import org.jetbrains.kotlin.backend.common.lower.optimizations.PropertyAccessorInlineLowering
@@ -125,6 +126,12 @@
     name = "LocalDeclarationsInInlineLambdasPreparationPhase",
 )
 
+private val popupLocalDeclarationsInInlineLambdasPhase = makeIrModulePhase(
+    ::LocalDeclarationsInInlineLambdasPopupLowering,
+    name = "LocalDeclarationsInInlineLambdasPopupLowering",
+    prerequisite = setOf(prepareLocalDeclarationsInInlineLambdasPhase),
+)
+
 /**
  * The first phase of inlining (inline only private functions).
  */
@@ -606,6 +613,7 @@
         lateinitPhase,
         sharedVariablesLoweringPhase,
         prepareLocalDeclarationsInInlineLambdasPhase,
+        popupLocalDeclarationsInInlineLambdasPhase,
         arrayConstructorPhase,
         inlineOnlyPrivateFunctionsPhase,
         outerThisSpecialAccessorInInlineFunctionsPhase,
diff --git a/compiler/ir/ir.inline/src/org/jetbrains/kotlin/ir/inline/CommonLoweringPhases.kt b/compiler/ir/ir.inline/src/org/jetbrains/kotlin/ir/inline/CommonLoweringPhases.kt
index 11ac647..114cce8a 100644
--- a/compiler/ir/ir.inline/src/org/jetbrains/kotlin/ir/inline/CommonLoweringPhases.kt
+++ b/compiler/ir/ir.inline/src/org/jetbrains/kotlin/ir/inline/CommonLoweringPhases.kt
@@ -13,6 +13,7 @@
 import org.jetbrains.kotlin.backend.common.lower.SharedVariablesLowering
 import org.jetbrains.kotlin.backend.common.lower.inline.AvoidLocalFOsInInlineFunctionsLowering
 import org.jetbrains.kotlin.backend.common.lower.inline.InlineCallCycleCheckerLowering
+import org.jetbrains.kotlin.backend.common.lower.inline.LocalDeclarationsInInlineLambdasPopupLowering
 import org.jetbrains.kotlin.backend.common.lower.inline.LocalDeclarationsInInlineLambdasPreparationLowering
 import org.jetbrains.kotlin.backend.common.phaser.IrValidationAfterInliningAllFunctionsPhase
 import org.jetbrains.kotlin.backend.common.phaser.IrValidationAfterInliningOnlyPrivateFunctionsPhase
@@ -44,6 +45,12 @@
     name = "LocalDeclarationsInInlineLambdasPreparationPhase",
 )
 
+private val popupLocalDeclarationsInInlineLambdasPhase = makeIrModulePhase(
+    ::LocalDeclarationsInInlineLambdasPopupLowering,
+    name = "LocalDeclarationsInInlineLambdasPopupPhase",
+    prerequisite = setOf(prepareLocalDeclarationsInInlineLambdasPhase)
+)
+
 private val arrayConstructorPhase = makeIrModulePhase(
     ::ArrayConstructorLowering,
     name = "ArrayConstructor",
@@ -145,6 +152,7 @@
         this += lateinitPhase
         this += sharedVariablesLoweringPhase
         this += prepareLocalDeclarationsInInlineLambdasPhase
+        this += popupLocalDeclarationsInInlineLambdasPhase
         this += arrayConstructorPhase
         this += checkInlineCallCyclesPhase
         this += inlineOnlyPrivateFunctionsPhase
diff --git a/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/util/AdditionalIrUtils.kt b/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/util/AdditionalIrUtils.kt
index c0ec4b9..db0eeb6 100644
--- a/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/util/AdditionalIrUtils.kt
+++ b/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/util/AdditionalIrUtils.kt
@@ -11,6 +11,7 @@
 import org.jetbrains.kotlin.descriptors.annotations.KotlinTarget
 import org.jetbrains.kotlin.ir.*
 import org.jetbrains.kotlin.ir.declarations.*
+import org.jetbrains.kotlin.ir.expressions.IrBlock
 import org.jetbrains.kotlin.ir.expressions.IrConstructorCall
 import org.jetbrains.kotlin.ir.expressions.IrGetEnumValue
 import org.jetbrains.kotlin.ir.expressions.IrVararg
@@ -220,6 +221,8 @@
  */
 var IrDeclaration.isOriginallyLocalDeclaration: Boolean by irFlag(copyByDefault = true)
 
+var IrBlock.isSyntheticBlockForInlineCall: Boolean by irFlag(copyByDefault = true)
+
 private inline fun IrDeclaration.isLocalImpl(isLocal: (IrDeclarationWithVisibility) -> Boolean): Boolean {
     var current: IrElement = this
     while (current !is IrPackageFragment) {
diff --git a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/driver/phases/NativeLoweringPhases.kt b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/driver/phases/NativeLoweringPhases.kt
index 3494034..be17494 100644
--- a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/driver/phases/NativeLoweringPhases.kt
+++ b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/driver/phases/NativeLoweringPhases.kt
@@ -11,6 +11,7 @@
 import org.jetbrains.kotlin.backend.common.ir.Symbols
 import org.jetbrains.kotlin.backend.common.lower.*
 import org.jetbrains.kotlin.backend.common.lower.coroutines.AddContinuationToNonLocalSuspendFunctionsLowering
+import org.jetbrains.kotlin.backend.common.lower.inline.LocalDeclarationsInInlineLambdasPopupLowering
 import org.jetbrains.kotlin.backend.common.lower.inline.LocalDeclarationsInInlineLambdasPreparationLowering
 import org.jetbrains.kotlin.backend.common.lower.optimizations.PropertyAccessorInlineLowering
 import org.jetbrains.kotlin.backend.common.lower.optimizations.LivenessAnalysis
@@ -158,6 +159,12 @@
         prerequisite = setOf(sharedVariablesPhase),
 )
 
+private val popupLocalDeclarationsInInlineLambdasPhase = createFileLoweringPhase(
+        ::LocalDeclarationsInInlineLambdasPopupLowering,
+        name = "PopupLocalDeclarationsInInlineLambdas",
+        prerequisite = setOf(prepareLocalDeclarationsInInlineLambdasPhase),
+)
+
 private val postInlinePhase = createFileLoweringPhase(
         { context: Context -> PostInlineLowering(context) },
         name = "PostInline",
@@ -378,7 +385,7 @@
             InteropLowering(generationState.context, generationState.fileLowerState)
         },
         name = "Interop",
-        prerequisite = setOf(prepareLocalDeclarationsInInlineLambdasPhase)
+        prerequisite = setOf(popupLocalDeclarationsInInlineLambdasPhase)
 )
 
 private val specialInteropIntrinsicsPhase = createFileLoweringPhase(
@@ -571,6 +578,7 @@
         lateinitPhase,
         sharedVariablesPhase,
         prepareLocalDeclarationsInInlineLambdasPhase,
+        popupLocalDeclarationsInInlineLambdasPhase,
         arrayConstructorPhase,
         inlineOnlyPrivateFunctionsPhase,
         outerThisSpecialAccessorInInlineFunctionsPhase,
diff --git a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/NativeInlineFunctionResolver.kt b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/NativeInlineFunctionResolver.kt
index b3e771f..e382a1e 100644
--- a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/NativeInlineFunctionResolver.kt
+++ b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/NativeInlineFunctionResolver.kt
@@ -9,6 +9,7 @@
 import org.jetbrains.kotlin.backend.common.lower.LateinitLowering
 import org.jetbrains.kotlin.backend.common.lower.SharedVariablesLowering
 import org.jetbrains.kotlin.backend.common.lower.UpgradeCallableReferences
+import org.jetbrains.kotlin.backend.common.lower.inline.LocalDeclarationsInInlineLambdasPopupLowering
 import org.jetbrains.kotlin.backend.common.lower.inline.LocalDeclarationsInInlineLambdasPreparationLowering
 import org.jetbrains.kotlin.backend.konan.Context
 import org.jetbrains.kotlin.backend.konan.NativeGenerationState
@@ -60,6 +61,7 @@
         SharedVariablesLowering(context).lower(body, function)
 
         LocalDeclarationsInInlineLambdasPreparationLowering(context).lower(body, function)
+        LocalDeclarationsInInlineLambdasPopupLowering(context).lower(body, function)
 
         ArrayConstructorLowering(context).lower(body, function)