JVM_IR: Backwards compatible handling of default tailrec params.
Before 1.4 tailrec function default arguments were evaluated right-to-left instead of left-to-right. This is controlled by a compile-time flag. This change adds support for the right-to-left evaluation order when that flag is not set.
This commit is contained in:
+37
-31
@@ -16,10 +16,15 @@
|
||||
|
||||
package org.jetbrains.kotlin.backend.common.lower
|
||||
|
||||
import org.jetbrains.kotlin.backend.common.*
|
||||
import org.jetbrains.kotlin.backend.common.phaser.makeIrFilePhase
|
||||
import org.jetbrains.kotlin.backend.common.BackendContext
|
||||
import org.jetbrains.kotlin.backend.common.FunctionLoweringPass
|
||||
import org.jetbrains.kotlin.backend.common.collectTailRecursionCalls
|
||||
import org.jetbrains.kotlin.backend.common.deepCopyWithVariables
|
||||
import org.jetbrains.kotlin.ir.builders.*
|
||||
import org.jetbrains.kotlin.ir.declarations.*
|
||||
import org.jetbrains.kotlin.ir.declarations.IrFunction
|
||||
import org.jetbrains.kotlin.ir.declarations.IrValueDeclaration
|
||||
import org.jetbrains.kotlin.ir.declarations.IrValueParameter
|
||||
import org.jetbrains.kotlin.ir.declarations.IrVariable
|
||||
import org.jetbrains.kotlin.ir.expressions.*
|
||||
import org.jetbrains.kotlin.ir.expressions.impl.IrGetValueImpl
|
||||
import org.jetbrains.kotlin.ir.symbols.IrValueParameterSymbol
|
||||
@@ -28,25 +33,21 @@ import org.jetbrains.kotlin.ir.util.getArgumentsWithIr
|
||||
import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid
|
||||
import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid
|
||||
|
||||
val tailrecPhase = makeIrFilePhase(
|
||||
::TailrecLowering,
|
||||
name = "Tailrec",
|
||||
description = "Handle tailrec calls"
|
||||
)
|
||||
|
||||
/**
|
||||
* This pass lowers tail recursion calls in `tailrec` functions.
|
||||
*
|
||||
* Note: it currently can't handle local functions and classes declared in default arguments.
|
||||
* See [deepCopyWithVariables].
|
||||
*/
|
||||
class TailrecLowering(val context: BackendContext) : FunctionLoweringPass {
|
||||
open class TailrecLowering(val context: BackendContext) : FunctionLoweringPass {
|
||||
override fun lower(irFunction: IrFunction) {
|
||||
lowerTailRecursionCalls(context, irFunction)
|
||||
lowerTailRecursionCalls(context, irFunction, useProperComputationOrderOfTailrecDefaultParameters())
|
||||
}
|
||||
|
||||
open fun useProperComputationOrderOfTailrecDefaultParameters() = true
|
||||
}
|
||||
|
||||
private fun lowerTailRecursionCalls(context: BackendContext, irFunction: IrFunction) {
|
||||
private fun lowerTailRecursionCalls(context: BackendContext, irFunction: IrFunction, properComputationOrderOfTailrecDefaultParameters: Boolean) {
|
||||
val tailRecursionCalls = collectTailRecursionCalls(irFunction)
|
||||
if (tailRecursionCalls.isEmpty()) {
|
||||
return
|
||||
@@ -77,7 +78,8 @@ private fun lowerTailRecursionCalls(context: BackendContext, irFunction: IrFunct
|
||||
|
||||
val transformer = BodyTransformer(
|
||||
builder, irFunction, loop,
|
||||
parameterToNew, parameterToVariable, tailRecursionCalls
|
||||
parameterToNew, parameterToVariable, tailRecursionCalls,
|
||||
properComputationOrderOfTailrecDefaultParameters
|
||||
)
|
||||
|
||||
oldBody.statements.forEach {
|
||||
@@ -96,7 +98,8 @@ private class BodyTransformer(
|
||||
val loop: IrLoop,
|
||||
val parameterToNew: Map<IrValueParameter, IrValueDeclaration>,
|
||||
val parameterToVariable: Map<IrValueParameter, IrVariable>,
|
||||
val tailRecursionCalls: Set<IrCall>
|
||||
val tailRecursionCalls: Set<IrCall>,
|
||||
val properComputationOrderOfTailrecDefaultParameters: Boolean
|
||||
) : IrElementTransformerVoid() {
|
||||
|
||||
val parameters = irFunction.explicitParameters
|
||||
@@ -133,28 +136,31 @@ private class BodyTransformer(
|
||||
val specifiedParameters = parameterToArgument.map { (parameter, _) -> parameter }.toSet()
|
||||
|
||||
// For each unspecified argument set the corresponding variable to default:
|
||||
parameters.filter { it !in specifiedParameters }.forEach { parameter ->
|
||||
parameters
|
||||
.filter { it !in specifiedParameters }
|
||||
.let { if (properComputationOrderOfTailrecDefaultParameters) it else it.asReversed() }
|
||||
.forEach { parameter ->
|
||||
|
||||
val originalDefaultValue = parameter.defaultValue?.expression ?: throw Error("no argument specified for $parameter")
|
||||
val originalDefaultValue = parameter.defaultValue?.expression ?: throw Error("no argument specified for $parameter")
|
||||
|
||||
// Copy default value, mapping parameters to variables containing freshly computed arguments:
|
||||
val defaultValue = originalDefaultValue
|
||||
.deepCopyWithVariables()
|
||||
.transform(object : IrElementTransformerVoid() {
|
||||
// Copy default value, mapping parameters to variables containing freshly computed arguments:
|
||||
val defaultValue = originalDefaultValue
|
||||
.deepCopyWithVariables()
|
||||
.transform(object : IrElementTransformerVoid() {
|
||||
|
||||
override fun visitGetValue(expression: IrGetValue): IrExpression {
|
||||
expression.transformChildrenVoid(this)
|
||||
override fun visitGetValue(expression: IrGetValue): IrExpression {
|
||||
expression.transformChildrenVoid(this)
|
||||
|
||||
val variable = parameterToVariable[expression.symbol.owner] ?: return expression
|
||||
return IrGetValueImpl(
|
||||
expression.startOffset, expression.endOffset, variable.type,
|
||||
variable.symbol, expression.origin
|
||||
)
|
||||
}
|
||||
}, data = null)
|
||||
val variable = parameterToVariable[expression.symbol.owner] ?: return expression
|
||||
return IrGetValueImpl(
|
||||
expression.startOffset, expression.endOffset, variable.type,
|
||||
variable.symbol, expression.origin
|
||||
)
|
||||
}
|
||||
}, data = null)
|
||||
|
||||
+irSetVar(parameterToVariable[parameter]!!.symbol, defaultValue)
|
||||
}
|
||||
+irSetVar(parameterToVariable[parameter]!!.symbol, defaultValue)
|
||||
}
|
||||
|
||||
// Jump to the entry:
|
||||
+irContinue(loop)
|
||||
|
||||
Reference in New Issue
Block a user