From 82f48cdd11e042106edaee93beb7bf511be11782 Mon Sep 17 00:00:00 2001 From: Mads Ager Date: Mon, 16 Dec 2019 14:16:14 +0100 Subject: [PATCH] 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. --- .../backend/common/lower/TailrecLowering.kt | 68 ++++++++++--------- .../jetbrains/kotlin/backend/jvm/JvmLower.kt | 6 ++ .../backend/jvm/lower/JvmTailrecLowering.kt | 16 +++++ .../defaultArgsWithSideEffectsOld.kt | 2 - 4 files changed, 59 insertions(+), 33 deletions(-) create mode 100644 compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/JvmTailrecLowering.kt diff --git a/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/TailrecLowering.kt b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/TailrecLowering.kt index 5ed6571ebaa..248d8024b75 100644 --- a/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/TailrecLowering.kt +++ b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/TailrecLowering.kt @@ -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, val parameterToVariable: Map, - val tailRecursionCalls: Set + val tailRecursionCalls: Set, + 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) diff --git a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/JvmLower.kt b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/JvmLower.kt index 9b8ca7b7b17..f7f0f6fd194 100644 --- a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/JvmLower.kt +++ b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/JvmLower.kt @@ -223,6 +223,12 @@ private val syntheticAccessorPhase = makeIrFilePhase( prerequisite = setOf(objectClassPhase, staticDefaultFunctionPhase, interfacePhase) ) +private val tailrecPhase = makeIrFilePhase( + ::JvmTailrecLowering, + name = "Tailrec", + description = "Handle tailrec calls" +) + @Suppress("Reformat") private val jvmFilePhases = typeAliasAnnotationMethodsPhase then diff --git a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/JvmTailrecLowering.kt b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/JvmTailrecLowering.kt new file mode 100644 index 00000000000..b90799ae761 --- /dev/null +++ b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/JvmTailrecLowering.kt @@ -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) +} \ No newline at end of file diff --git a/compiler/testData/codegen/box/diagnostics/functions/tailRecursion/defaultArgsWithSideEffectsOld.kt b/compiler/testData/codegen/box/diagnostics/functions/tailRecursion/defaultArgsWithSideEffectsOld.kt index adddd178550..7559a02d39e 100644 --- a/compiler/testData/codegen/box/diagnostics/functions/tailRecursion/defaultArgsWithSideEffectsOld.kt +++ b/compiler/testData/codegen/box/diagnostics/functions/tailRecursion/defaultArgsWithSideEffectsOld.kt @@ -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"