[Inliner] Fixed bug with incorrect eval order

We need to extract to temp locals all bound arguments
of function reference parameters of an inline call to
preserve the correct evaluation order
This commit is contained in:
Igor Chevdar
2019-07-09 10:30:07 +03:00
committed by Igor Chevdar
parent 1b49ea8465
commit 8c8acbc626
3 changed files with 81 additions and 9 deletions
@@ -223,6 +223,7 @@ internal class FunctionInlining(val context: Context) : IrElementTransformerVoid
return super.visitCall(expression) return super.visitCall(expression)
if (functionArgument is IrFunctionReference) { if (functionArgument is IrFunctionReference) {
functionArgument.transformChildrenVoid(this)
val function = functionArgument.symbol.owner val function = functionArgument.symbol.owner
val functionParameters = function.explicitParameters val functionParameters = function.explicitParameters
val boundFunctionParameters = functionArgument.getArgumentsWithIr() val boundFunctionParameters = functionArgument.getArgumentsWithIr()
@@ -241,10 +242,10 @@ internal class FunctionInlining(val context: Context) : IrElementTransformerVoid
}.apply { }.apply {
functionParameters.forEach { functionParameters.forEach {
val argument = val argument =
if (!unboundArgsSet.contains(it)) if (unboundArgsSet.contains(it))
boundFunctionParametersMap[it]!!
else
valueParameters[unboundIndex++].second valueParameters[unboundIndex++].second
else
boundFunctionParametersMap[it]!!
when (it) { when (it) {
function.dispatchReceiverParameter -> function.dispatchReceiverParameter ->
this.dispatchReceiver = argument.implicitCastIfNeededTo(function.dispatchReceiverParameter!!.type) this.dispatchReceiver = argument.implicitCastIfNeededTo(function.dispatchReceiverParameter!!.type)
@@ -318,8 +319,7 @@ internal class FunctionInlining(val context: Context) : IrElementTransformerVoid
} }
} }
//-------------------------------------------------------------------------// // callee might be a copied version of callsite.symbol.owner
private fun buildParameterToArgument(callSite: IrFunctionAccessExpression, callee: IrFunction): List<ParameterToArgument> { private fun buildParameterToArgument(callSite: IrFunctionAccessExpression, callee: IrFunction): List<ParameterToArgument> {
val parameterToArgument = mutableListOf<ParameterToArgument>() val parameterToArgument = mutableListOf<ParameterToArgument>()
@@ -395,12 +395,38 @@ internal class FunctionInlining(val context: Context) : IrElementTransformerVoid
//-------------------------------------------------------------------------// //-------------------------------------------------------------------------//
private fun evaluateArguments(callSite: IrFunctionAccessExpression, callee: IrFunction): List<IrStatement> { private fun evaluateArguments(functionReference: IrFunctionReference): List<IrStatement> {
val arguments = functionReference.getArgumentsWithIr().map { ParameterToArgument(it.first, it.second) }
val parameterToArgumentOld = buildParameterToArgument(callSite, callee)
val evaluationStatements = mutableListOf<IrStatement>() val evaluationStatements = mutableListOf<IrStatement>()
val substitutor = ParameterSubstitutor() val substitutor = ParameterSubstitutor()
parameterToArgumentOld.forEach { val referenced = functionReference.symbol.owner
arguments.forEach {
val newArgument = if (it.isImmutableVariableLoad) {
it.argumentExpression.transform(substitutor, data = null) // Arguments may reference the previous ones - substitute them.
} else {
val newVariable = currentScope.scope.createTemporaryVariableWithWrappedDescriptor( // Create new variable and init it with the parameter expression.
irExpression = it.argumentExpression.transform(substitutor, data = null), // Arguments may reference the previous ones - substitute them.
nameHint = callee.symbol.owner.name.toString(),
isMutable = false)
evaluationStatements.add(newVariable)
IrGetValueImpl(it.argumentExpression.startOffset, it.argumentExpression.endOffset, newVariable.symbol)
}
when (it.parameter) {
referenced.dispatchReceiverParameter -> functionReference.dispatchReceiver = newArgument
referenced.extensionReceiverParameter -> functionReference.extensionReceiver = newArgument
else -> functionReference.putValueArgument(it.parameter.index, newArgument)
}
}
return evaluationStatements
}
private fun evaluateArguments(callSite: IrFunctionAccessExpression, callee: IrFunction): List<IrStatement> {
val arguments = buildParameterToArgument(callSite, callee)
val evaluationStatements = mutableListOf<IrStatement>()
val substitutor = ParameterSubstitutor()
arguments.forEach {
/* /*
* We need to create temporary variable for each argument except inlinable lambda arguments. * 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, * For simplicity and to produce simpler IR we don't create temporaries for every immutable variable,
@@ -408,6 +434,7 @@ internal class FunctionInlining(val context: Context) : IrElementTransformerVoid
*/ */
if (it.isInlinableLambdaArgument) { if (it.isInlinableLambdaArgument) {
substituteMap[it.parameter] = it.argumentExpression substituteMap[it.parameter] = it.argumentExpression
(it.argumentExpression as? IrFunctionReference)?.let { evaluationStatements += evaluateArguments(it) }
return@forEach return@forEach
} }
+5
View File
@@ -2874,6 +2874,11 @@ task inline_genericFunctionReference(type: KonanLocalTest) {
source = "codegen/inline/genericFunctionReference.kt" source = "codegen/inline/genericFunctionReference.kt"
} }
task inline_correctOrderFunctionReference(type: KonanLocalTest) {
goldValue = "OK\n"
source = "codegen/inline/correctOrderFunctionReference.kt"
}
task classDeclarationInsideInline(type: KonanLocalTest) { task classDeclarationInsideInline(type: KonanLocalTest) {
source = "codegen/inline/classDeclarationInsideInline.kt" source = "codegen/inline/classDeclarationInsideInline.kt"
goldValue = "test1: 1.0\n1\ntest2\n" goldValue = "test1: 1.0\n1\ntest2\n"
@@ -0,0 +1,40 @@
/*
* Copyright 2010-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the LICENSE file.
*/
package codegen.inline.correctOrderFunctionReference
import kotlin.test.*
class Foo(val a: String) {
fun test() = a
}
inline fun test(a: String, b: () -> String, c: () -> String, d: () -> String, e: String): String {
return a + b() + c() + d() + e
}
var effects = ""
fun create(a: String): Foo {
effects += a
return Foo(a)
}
fun create2(a: String, f: () -> String): Foo {
effects += a
return Foo(a)
}
fun box(): String {
val result = test(create("A").a, create("B")::a, create("C")::test, create2("E", create("D")::test)::test, create("F").a)
if (effects != "ABCDEF") return "fail 1: $effects"
return if (result == "ABCEF") "OK" else "fail 2: $result"
}
@Test fun runTest() {
println(box())
}