[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:
committed by
Igor Chevdar
parent
1b49ea8465
commit
8c8acbc626
+36
-9
@@ -223,6 +223,7 @@ internal class FunctionInlining(val context: Context) : IrElementTransformerVoid
|
||||
return super.visitCall(expression)
|
||||
|
||||
if (functionArgument is IrFunctionReference) {
|
||||
functionArgument.transformChildrenVoid(this)
|
||||
val function = functionArgument.symbol.owner
|
||||
val functionParameters = function.explicitParameters
|
||||
val boundFunctionParameters = functionArgument.getArgumentsWithIr()
|
||||
@@ -241,10 +242,10 @@ internal class FunctionInlining(val context: Context) : IrElementTransformerVoid
|
||||
}.apply {
|
||||
functionParameters.forEach {
|
||||
val argument =
|
||||
if (!unboundArgsSet.contains(it))
|
||||
boundFunctionParametersMap[it]!!
|
||||
else
|
||||
if (unboundArgsSet.contains(it))
|
||||
valueParameters[unboundIndex++].second
|
||||
else
|
||||
boundFunctionParametersMap[it]!!
|
||||
when (it) {
|
||||
function.dispatchReceiverParameter ->
|
||||
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> {
|
||||
|
||||
val parameterToArgument = mutableListOf<ParameterToArgument>()
|
||||
@@ -395,12 +395,38 @@ internal class FunctionInlining(val context: Context) : IrElementTransformerVoid
|
||||
|
||||
//-------------------------------------------------------------------------//
|
||||
|
||||
private fun evaluateArguments(callSite: IrFunctionAccessExpression, callee: IrFunction): List<IrStatement> {
|
||||
|
||||
val parameterToArgumentOld = buildParameterToArgument(callSite, callee)
|
||||
private fun evaluateArguments(functionReference: IrFunctionReference): List<IrStatement> {
|
||||
val arguments = functionReference.getArgumentsWithIr().map { ParameterToArgument(it.first, it.second) }
|
||||
val evaluationStatements = mutableListOf<IrStatement>()
|
||||
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.
|
||||
* 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) {
|
||||
substituteMap[it.parameter] = it.argumentExpression
|
||||
(it.argumentExpression as? IrFunctionReference)?.let { evaluationStatements += evaluateArguments(it) }
|
||||
return@forEach
|
||||
}
|
||||
|
||||
|
||||
@@ -2874,6 +2874,11 @@ task inline_genericFunctionReference(type: KonanLocalTest) {
|
||||
source = "codegen/inline/genericFunctionReference.kt"
|
||||
}
|
||||
|
||||
task inline_correctOrderFunctionReference(type: KonanLocalTest) {
|
||||
goldValue = "OK\n"
|
||||
source = "codegen/inline/correctOrderFunctionReference.kt"
|
||||
}
|
||||
|
||||
task classDeclarationInsideInline(type: KonanLocalTest) {
|
||||
source = "codegen/inline/classDeclarationInsideInline.kt"
|
||||
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())
|
||||
}
|
||||
Reference in New Issue
Block a user