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)
|
||||
|
||||
@@ -223,6 +223,12 @@ private val syntheticAccessorPhase = makeIrFilePhase(
|
||||
prerequisite = setOf(objectClassPhase, staticDefaultFunctionPhase, interfacePhase)
|
||||
)
|
||||
|
||||
private val tailrecPhase = makeIrFilePhase<JvmBackendContext>(
|
||||
::JvmTailrecLowering,
|
||||
name = "Tailrec",
|
||||
description = "Handle tailrec calls"
|
||||
)
|
||||
|
||||
@Suppress("Reformat")
|
||||
private val jvmFilePhases =
|
||||
typeAliasAnnotationMethodsPhase then
|
||||
|
||||
+16
@@ -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)
|
||||
}
|
||||
Vendored
-2
@@ -1,7 +1,5 @@
|
||||
// !LANGUAGE: -ProperComputationOrderOfTailrecDefaultParameters
|
||||
// IGNORE_BACKEND_FIR: JVM_IR
|
||||
// TARGET_BACKEND: JVM
|
||||
// IGNORE_BACKEND: JVM_IR
|
||||
|
||||
var counter = 0
|
||||
fun calc(counter: Int) = if (counter % 2 == 0) "K" else "O"
|
||||
|
||||
Reference in New Issue
Block a user