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 98f0807db1b..286868b79f6 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 @@ class FunctionInlining( 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 @@ class FunctionInlining( val evaluationStatements = mutableListOf() 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 4b58e0c846d..53ad8a101d5 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 @@ interface IrDeclarationOrigin { 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 7957472ee7d..91e23d76944 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 @@ private val inlinePhase = createFileLoweringPhase( 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 70253e3b6a7..5b242d72797 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 @@ internal class CodeGeneratorVisitor( } } -private fun IrValueParameter.debugNameConversion() = descriptor.name.debugNameConversion() - -private fun IrVariable.debugNameConversion() = descriptor.name.debugNameConversion() - private val thisName = Name.special("") 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 @@ private val underscoreThisName = Name.identifier("_this") * 1. 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 e1d9e76bdeb..e60c9c78aa5 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 @@ class LldbTests { """.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 + """) +}