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
|
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
|
||||||
|
|||||||
+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
|
// !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"
|
||||||
|
|||||||
Reference in New Issue
Block a user