Add the option to create temporary variables for all inlined arguments
diff --git a/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/inline/FunctionInlining.kt b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/inline/FunctionInlining.kt
index 98f0807..286868b 100644
--- a/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/inline/FunctionInlining.kt
+++ b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/inline/FunctionInlining.kt
@@ -83,7 +83,8 @@
val context: CommonBackendContext,
val inlineFunctionResolver: InlineFunctionResolver,
val innerClassesSupport: InnerClassesSupport? = null,
- val insertAdditionalImplicitCasts: Boolean = false
+ val insertAdditionalImplicitCasts: Boolean = false,
+ val alwaysCreateTemporaryVariablesForArguments: Boolean = false
) : IrElementTransformerVoidWithContext(), BodyLoweringPass {
constructor(context: CommonBackendContext) : this(context, DefaultInlineFunctionResolver(context), null)
@@ -590,55 +591,63 @@
val evaluationStatements = mutableListOf<IrStatement>()
val substitutor = ParameterSubstitutor()
arguments.forEach { argument ->
+ val parameter = argument.parameter
/*
* We need to create temporary variable for each argument except inlinable lambda arguments.
* For simplicity and to produce simpler IR we don't create temporaries for every immutable variable,
* not only for those referring to inlinable lambdas.
*/
if (argument.isInlinableLambdaArgument) {
- substituteMap[argument.parameter] = argument.argumentExpression
+ substituteMap[parameter] = argument.argumentExpression
(argument.argumentExpression as? IrFunctionReference)?.let { evaluationStatements += evaluateArguments(it) }
return@forEach
}
- if (argument.isImmutableVariableLoad) {
- substituteMap[argument.parameter] =
- argument.argumentExpression.transform( // Arguments may reference the previous ones - substitute them.
- substitutor,
- data = null
- )
- return@forEach
- }
-
// Arguments may reference the previous ones - substitute them.
val variableInitializer = argument.argumentExpression.transform(substitutor, data = null)
-
- val argumentExtracted = !argument.argumentExpression.isPure(false, context = context)
-
- if (!argumentExtracted) {
- substituteMap[argument.parameter] = variableInitializer
- } else {
- val newVariable =
- currentScope.scope.createTemporaryVariable(
- irExpression = IrBlockImpl(
- variableInitializer.startOffset,
- variableInitializer.endOffset,
- variableInitializer.type,
- InlinerExpressionLocationHint((currentScope.irElement as IrSymbolOwner).symbol)
- ).apply {
- statements.add(variableInitializer)
- },
- nameHint = callee.symbol.owner.name.toString(),
- isMutable = false
- )
-
+ val shouldCreateTemporaryVariable =
+ (alwaysCreateTemporaryVariablesForArguments && !parameter.isInlineParameter()) ||
+ argument.shouldBeSubstitutedViaTemporaryVariable()
+ if (shouldCreateTemporaryVariable) {
+ val newVariable = createTemporaryVariable(parameter, variableInitializer, callee)
evaluationStatements.add(newVariable)
- substituteMap[argument.parameter] = IrGetValueWithoutLocation(newVariable.symbol)
+ substituteMap[parameter] = IrGetValueWithoutLocation(newVariable.symbol)
+ } else {
+ substituteMap[parameter] = variableInitializer
}
}
return evaluationStatements
}
+
+ private fun ParameterToArgument.shouldBeSubstitutedViaTemporaryVariable(): Boolean =
+ !isImmutableVariableLoad && !argumentExpression.isPure(false, context = context)
+
+ private fun createTemporaryVariable(parameter: IrValueParameter, variableInitializer: IrExpression, callee: IrFunction): IrVariable {
+ val variable = currentScope.scope.createTemporaryVariable(
+ irExpression = IrBlockImpl(
+ variableInitializer.startOffset,
+ variableInitializer.endOffset,
+ variableInitializer.type,
+ InlinerExpressionLocationHint((currentScope.irElement as IrSymbolOwner).symbol)
+ ).apply {
+ statements.add(variableInitializer)
+ },
+ nameHint = callee.symbol.owner.name.toString(),
+ isMutable = false,
+ origin = if (parameter == callee.extensionReceiverParameter) {
+ IrDeclarationOrigin.IR_TEMPORARY_VARIABLE_FOR_INLINED_EXTENSION_RECEIVER
+ } else {
+ IrDeclarationOrigin.IR_TEMPORARY_VARIABLE_FOR_INLINED_PARAMETER
+ }
+ )
+
+ if (alwaysCreateTemporaryVariablesForArguments) {
+ variable.name = parameter.name
+ }
+
+ return variable
+ }
}
private class IrGetValueWithoutLocation(
diff --git a/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/declarations/IrDeclarationOrigin.kt b/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/declarations/IrDeclarationOrigin.kt
index 4b58e0c..53ad8a1 100644
--- a/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/declarations/IrDeclarationOrigin.kt
+++ b/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/declarations/IrDeclarationOrigin.kt
@@ -52,6 +52,8 @@
object INSTANCE_RECEIVER : IrDeclarationOriginImpl("INSTANCE_RECEIVER")
object PRIMARY_CONSTRUCTOR_PARAMETER : IrDeclarationOriginImpl("PRIMARY_CONSTRUCTOR_PARAMETER")
object IR_TEMPORARY_VARIABLE : IrDeclarationOriginImpl("IR_TEMPORARY_VARIABLE")
+ object IR_TEMPORARY_VARIABLE_FOR_INLINED_PARAMETER : IrDeclarationOriginImpl("IR_TEMPORARY_VARIABLE_FOR_INLINED_PARAMETER")
+ object IR_TEMPORARY_VARIABLE_FOR_INLINED_EXTENSION_RECEIVER : IrDeclarationOriginImpl("IR_TEMPORARY_VARIABLE_FOR_INLINED_EXTENSION_RECEIVER")
object IR_EXTERNAL_DECLARATION_STUB : IrDeclarationOriginImpl("IR_EXTERNAL_DECLARATION_STUB")
object IR_EXTERNAL_JAVA_DECLARATION_STUB : IrDeclarationOriginImpl("IR_EXTERNAL_JAVA_DECLARATION_STUB")
object IR_BUILTINS_STUB : IrDeclarationOriginImpl("IR_BUILTINS_STUB")
diff --git a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/driver/phases/Lowerings.kt b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/driver/phases/Lowerings.kt
index 7957472..91e23d7 100644
--- a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/driver/phases/Lowerings.kt
+++ b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/driver/phases/Lowerings.kt
@@ -321,7 +321,11 @@
lowering = { context: NativeGenerationState ->
object : FileLoweringPass {
override fun lower(irFile: IrFile) {
- FunctionInlining(context.context, NativeInlineFunctionResolver(context.context, context)).lower(irFile)
+ FunctionInlining(
+ context.context,
+ NativeInlineFunctionResolver(context.context, context),
+ alwaysCreateTemporaryVariablesForArguments = context.shouldContainDebugInfo()
+ ).lower(irFile)
}
}
},
diff --git a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/IrToBitcode.kt b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/IrToBitcode.kt
index 70253e3..5b242d7 100644
--- a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/IrToBitcode.kt
+++ b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/IrToBitcode.kt
@@ -2905,12 +2905,10 @@
}
}
-private fun IrValueParameter.debugNameConversion() = descriptor.name.debugNameConversion()
-
-private fun IrVariable.debugNameConversion() = descriptor.name.debugNameConversion()
-
private val thisName = Name.special("<this>")
private val underscoreThisName = Name.identifier("_this")
+private val doubleUnderscoreThisName = Name.identifier("__this")
+
/**
* HACK: this is workaround for GH-2316, to let IDE some how operate with this.
* We're experiencing issue with libclang which is used as compiler of expression in lldb
@@ -2918,9 +2916,15 @@
* 1. <this> isn't accepted by libclang as valid variable name.
* 2. this is reserved name and compiled in special way.
*/
-private fun Name.debugNameConversion(): Name = when(this) {
- thisName -> underscoreThisName
- else -> this
+private fun IrValueDeclaration.debugNameConversion(): Name {
+ val name = descriptor.name
+ if (name == thisName) {
+ return when (origin) {
+ IrDeclarationOrigin.IR_TEMPORARY_VARIABLE_FOR_INLINED_EXTENSION_RECEIVER -> doubleUnderscoreThisName
+ else -> underscoreThisName
+ }
+ }
+ return name
}
internal class LocationInfo(val scope: DIScopeOpaqueRef,
diff --git a/kotlin-native/backend.native/tests/debugger/src/test/kotlin/org/jetbrains/kotlin/native/test/debugger/LldbTests.kt b/kotlin-native/backend.native/tests/debugger/src/test/kotlin/org/jetbrains/kotlin/native/test/debugger/LldbTests.kt
index e1d9e76..e60c9c7 100644
--- a/kotlin-native/backend.native/tests/debugger/src/test/kotlin/org/jetbrains/kotlin/native/test/debugger/LldbTests.kt
+++ b/kotlin-native/backend.native/tests/debugger/src/test/kotlin/org/jetbrains/kotlin/native/test/debugger/LldbTests.kt
@@ -495,4 +495,261 @@
""".trimIndent().lldb(program)
}
-}
\ No newline at end of file
+ @Test
+ fun `inline function arguments as expressions are visible`() = lldbTest("""
+ inline fun foo(
+ p1: Int, p2: Int, p3: Int, p4: Int, p5: Int, p6: Int, p7: Int,
+ f: (Int, Int, Int, Int, Int, Int, Int) -> Unit
+ ) {
+ println()
+ f(p1, p2, p3, p4, p5, p6, p7)
+ }
+
+ fun bar() = 3
+ inline fun baz() = 3
+ fun getCondition() = true
+ fun getNull(): Int? = null
+
+ const val X = 10
+
+ fun main() {
+ val tmp = bar()
+ foo(
+ 1,
+ tmp,
+ tmp + 2,
+ bar(),
+ X,
+ if (getCondition()) 1 else 2,
+ when {
+ tmp >= 0 -> 1
+ tmp < 0 -> 2
+ else -> 0
+ }
+ ) { p1, p2, p3, p4, p5, p6, p7 ->
+ println(p1 + p2 + p3 + p4 + p5 + p6 + p7)
+ }
+
+ foo(
+ { 2 + 2 }(),
+ baz(),
+ listOf(1, 2, 3).filter { it > 2 }.sum(),
+ getNull()?.let { it + 1 } ?: 0,
+ try { bar() } finally { baz() },
+ "".length,
+ object : Any() {
+ override fun hashCode(): Int {
+ return 1
+ }
+ }.hashCode()
+ ) { p1, p2, p3, p4, p5, p6, p7 ->
+ println(p1 + p2 + p3 + p4 + p5 + p6 + p7)
+ }
+ }
+ """, """
+ > b main.kt:5
+ > b main.kt:31
+ > b main.kt:47
+ > ${lldbCommandRunOrContinue()}
+ > v
+ (int) p1 = 1
+ (int) p2 = 3
+ (int) p3 = 5
+ (int) p4 = 3
+ (int) p5 = 10
+ (int) p6 = 1
+ (int) p7 = 1
+ > c
+ > v
+ (int) p1 = 1
+ (int) p2 = 3
+ (int) p3 = 5
+ (int) p4 = 3
+ (int) p5 = 10
+ (int) p6 = 1
+ (int) p7 = 1
+ > c
+ > v
+ (int) p1 = 4
+ (int) p2 = 3
+ (int) p3 = 3
+ (int) p4 = 0
+ (int) p5 = 3
+ (int) p6 = 0
+ (int) p7 = 1
+ > c
+ > v
+ (int) p1 = 4
+ (int) p2 = 3
+ (int) p3 = 3
+ (int) p4 = 0
+ (int) p5 = 3
+ (int) p6 = 0
+ (int) p7 = 1
+ > q
+ """)
+
+ @Test
+ fun `inline lambda anonymous argument is visible`() = lldbTest("""
+ fun main() {
+ val list = listOf(1)
+ list.filter {
+ it > 2
+ }
+
+ list.map {
+ it * 2
+ }
+
+ list.find {
+ it == 1
+ }
+ }
+ """, """
+ > b main.kt:4
+ > b main.kt:8
+ > b main.kt:12
+ > ${lldbCommandRunOrContinue()}
+ > v
+ (int) it = 1
+ > c
+ > v
+ (int) it = 1
+ > c
+ > v
+ (int) it = 1
+ > q
+ """)
+
+ @Test
+ fun `inline function arguments of various types are visible`() = lldbTest("""
+ class A
+ object B
+ data class C(val x: Int)
+ interface I
+
+ inline fun foo(a: A, b: B, c: C, i: I, f: (A, B, C, I) -> Unit) {
+ println()
+ f(a, b, c, i)
+ }
+
+ fun main() {
+ val a = A()
+ val b = B
+ val c = C(0)
+ val i = object : I {}
+ foo(a, b, c, i) { pa, pb, pc, pi ->
+ println(pa)
+ println(pb)
+ println(pc)
+ println(pi)
+ }
+
+ foo(A(), B, C(0), object : I {}) { pa, pb, pc, pi ->
+ println(pa)
+ println(pb)
+ println(pc)
+ println(pi)
+ }
+ }
+ """, """
+ > b main.kt:7
+ > b main.kt:17
+ > b main.kt:24
+ > ${lldbCommandRunOrContinue()}
+ > v
+ (ObjHeader *) a = []
+ (ObjHeader *) b = []
+ (ObjHeader *) c = [x: ...]
+ (ObjHeader *) i = []
+ > c
+ > v
+ (ObjHeader *) pa = []
+ (ObjHeader *) pb = []
+ (ObjHeader *) pc = [x: ...]
+ (ObjHeader *) pi = []
+ > c
+ > v
+ (ObjHeader *) a = []
+ (ObjHeader *) b = []
+ (ObjHeader *) c = [x: ...]
+ (ObjHeader *) i = []
+ > c
+ > v
+ (ObjHeader *) pa = []
+ (ObjHeader *) pb = []
+ (ObjHeader *) pc = [x: ...]
+ (ObjHeader *) pi = []
+ > q
+ """)
+
+ @Test
+ fun `inline function default parameters are visible`() = lldbTest("""
+ inline fun foo(x: Int = 0, y: String = "STRING", z: Any? = null) {
+ println(x)
+ println(y)
+ println(z)
+ }
+
+ fun main() {
+ foo()
+ foo(1)
+ foo(1, "TEST_STRING")
+ foo(1, "TEST_STRING", Any())
+ }
+ """, """
+ > b main.kt:2
+ > ${lldbCommandRunOrContinue()}
+ > v
+ (int) x = 0
+ (ObjHeader *) y = STRING
+ (ObjHeader *) z = NULL
+ > c
+ > v
+ (int) x = 1
+ (ObjHeader *) y = STRING
+ (ObjHeader *) z = NULL
+ > c
+ > v
+ (int) x = 1
+ (ObjHeader *) y = TEST_STRING
+ (ObjHeader *) z = NULL
+ > c
+ > v
+ (int) x = 1
+ (ObjHeader *) y = TEST_STRING
+ (ObjHeader *) z = []
+ > q
+ """)
+
+ @Test
+ fun `inline function extension receiver is visible`() = lldbTest("""
+ class A {
+ inline fun String.foo(f: String.() -> Unit) {
+ println()
+ f()
+ }
+
+ fun bar() {
+ "TEST".foo {
+ println()
+ }
+ }
+ }
+
+ fun main() {
+ A().bar()
+ }
+ """, """
+ > b main.kt:3
+ > b main.kt:9
+ > ${lldbCommandRunOrContinue()}
+ > v
+ (ObjHeader *) _this = []
+ (ObjHeader *) __this = TEST
+ > c
+ > v
+ (ObjHeader *) ${"$"}this${"$"}foo = TEST
+ > q
+ """)
+}