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:
Mads Ager
2019-12-16 14:16:14 +01:00
committed by max-kammerer
parent 90a1b15b77
commit 82f48cdd11
4 changed files with 59 additions and 33 deletions
@@ -16,10 +16,15 @@
package org.jetbrains.kotlin.backend.common.lower package org.jetbrains.kotlin.backend.common.lower
import org.jetbrains.kotlin.backend.common.* import org.jetbrains.kotlin.backend.common.BackendContext
import org.jetbrains.kotlin.backend.common.phaser.makeIrFilePhase 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.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.*
import org.jetbrains.kotlin.ir.expressions.impl.IrGetValueImpl import org.jetbrains.kotlin.ir.expressions.impl.IrGetValueImpl
import org.jetbrains.kotlin.ir.symbols.IrValueParameterSymbol 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.IrElementTransformerVoid
import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid 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. * This pass lowers tail recursion calls in `tailrec` functions.
* *
* Note: it currently can't handle local functions and classes declared in default arguments. * Note: it currently can't handle local functions and classes declared in default arguments.
* See [deepCopyWithVariables]. * See [deepCopyWithVariables].
*/ */
class TailrecLowering(val context: BackendContext) : FunctionLoweringPass { open class TailrecLowering(val context: BackendContext) : FunctionLoweringPass {
override fun lower(irFunction: IrFunction) { 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) val tailRecursionCalls = collectTailRecursionCalls(irFunction)
if (tailRecursionCalls.isEmpty()) { if (tailRecursionCalls.isEmpty()) {
return return
@@ -77,7 +78,8 @@ private fun lowerTailRecursionCalls(context: BackendContext, irFunction: IrFunct
val transformer = BodyTransformer( val transformer = BodyTransformer(
builder, irFunction, loop, builder, irFunction, loop,
parameterToNew, parameterToVariable, tailRecursionCalls parameterToNew, parameterToVariable, tailRecursionCalls,
properComputationOrderOfTailrecDefaultParameters
) )
oldBody.statements.forEach { oldBody.statements.forEach {
@@ -96,7 +98,8 @@ private class BodyTransformer(
val loop: IrLoop, val loop: IrLoop,
val parameterToNew: Map<IrValueParameter, IrValueDeclaration>, val parameterToNew: Map<IrValueParameter, IrValueDeclaration>,
val parameterToVariable: Map<IrValueParameter, IrVariable>, val parameterToVariable: Map<IrValueParameter, IrVariable>,
val tailRecursionCalls: Set<IrCall> val tailRecursionCalls: Set<IrCall>,
val properComputationOrderOfTailrecDefaultParameters: Boolean
) : IrElementTransformerVoid() { ) : IrElementTransformerVoid() {
val parameters = irFunction.explicitParameters val parameters = irFunction.explicitParameters
@@ -133,28 +136,31 @@ private class BodyTransformer(
val specifiedParameters = parameterToArgument.map { (parameter, _) -> parameter }.toSet() val specifiedParameters = parameterToArgument.map { (parameter, _) -> parameter }.toSet()
// For each unspecified argument set the corresponding variable to default: // 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: // Copy default value, mapping parameters to variables containing freshly computed arguments:
val defaultValue = originalDefaultValue val defaultValue = originalDefaultValue
.deepCopyWithVariables() .deepCopyWithVariables()
.transform(object : IrElementTransformerVoid() { .transform(object : IrElementTransformerVoid() {
override fun visitGetValue(expression: IrGetValue): IrExpression { override fun visitGetValue(expression: IrGetValue): IrExpression {
expression.transformChildrenVoid(this) expression.transformChildrenVoid(this)
val variable = parameterToVariable[expression.symbol.owner] ?: return expression val variable = parameterToVariable[expression.symbol.owner] ?: return expression
return IrGetValueImpl( return IrGetValueImpl(
expression.startOffset, expression.endOffset, variable.type, expression.startOffset, expression.endOffset, variable.type,
variable.symbol, expression.origin variable.symbol, expression.origin
) )
} }
}, data = null) }, data = null)
+irSetVar(parameterToVariable[parameter]!!.symbol, defaultValue) +irSetVar(parameterToVariable[parameter]!!.symbol, defaultValue)
} }
// Jump to the entry: // Jump to the entry:
+irContinue(loop) +irContinue(loop)
@@ -223,6 +223,12 @@ private val syntheticAccessorPhase = makeIrFilePhase(
prerequisite = setOf(objectClassPhase, staticDefaultFunctionPhase, interfacePhase) prerequisite = setOf(objectClassPhase, staticDefaultFunctionPhase, interfacePhase)
) )
private val tailrecPhase = makeIrFilePhase<JvmBackendContext>(
::JvmTailrecLowering,
name = "Tailrec",
description = "Handle tailrec calls"
)
@Suppress("Reformat") @Suppress("Reformat")
private val jvmFilePhases = private val jvmFilePhases =
typeAliasAnnotationMethodsPhase then typeAliasAnnotationMethodsPhase then
@@ -0,0 +1,16 @@
/*
* Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.backend.jvm.lower
import org.jetbrains.kotlin.backend.common.lower.TailrecLowering
import org.jetbrains.kotlin.backend.jvm.JvmBackendContext
import org.jetbrains.kotlin.config.LanguageFeature
import org.jetbrains.kotlin.config.languageVersionSettings
class JvmTailrecLowering(context: JvmBackendContext) : TailrecLowering(context) {
override fun useProperComputationOrderOfTailrecDefaultParameters(): Boolean =
context.ir.context.configuration.languageVersionSettings.supportsFeature(LanguageFeature.ProperComputationOrderOfTailrecDefaultParameters)
}
@@ -1,7 +1,5 @@
// !LANGUAGE: -ProperComputationOrderOfTailrecDefaultParameters // !LANGUAGE: -ProperComputationOrderOfTailrecDefaultParameters
// IGNORE_BACKEND_FIR: JVM_IR
// TARGET_BACKEND: JVM // TARGET_BACKEND: JVM
// IGNORE_BACKEND: JVM_IR
var counter = 0 var counter = 0
fun calc(counter: Int) = if (counter % 2 == 0) "K" else "O" fun calc(counter: Int) = if (counter % 2 == 0) "K" else "O"