IR: make calls with explicit singleton receiver tailrec [KT-48602]

This commit is contained in:
Tianyu Geng
2021-09-03 15:31:30 -07:00
committed by TeamCityServer
parent 8525b4932b
commit 3c84fbcab1
14 changed files with 143 additions and 28 deletions
@@ -20,7 +20,9 @@ import org.jetbrains.kotlin.fir.expressions.impl.FirNoReceiverExpression
import org.jetbrains.kotlin.fir.expressions.toResolvedCallableSymbol
import org.jetbrains.kotlin.fir.resolve.dfa.cfg.*
import org.jetbrains.kotlin.fir.resolve.dfa.controlFlowGraph
import org.jetbrains.kotlin.fir.symbols.impl.FirClassSymbol
import org.jetbrains.kotlin.fir.symbols.impl.FirNamedFunctionSymbol
import org.jetbrains.kotlin.fir.types.toSymbol
object FirTailrecFunctionChecker : FirSimpleFunctionChecker() {
override fun check(declaration: FirSimpleFunction, context: CheckerContext, reporter: DiagnosticReporter) {
@@ -80,8 +82,8 @@ object FirTailrecFunctionChecker : FirSimpleFunctionChecker() {
}
val dispatchReceiver = functionCall.dispatchReceiver
// A tailrec call does not support changing dispatchers. Here we report changing dispatch receiver if the dispatch receiver
// is present and not a `this`. For the `this` check, we don't need to actually compare if the dispatch receiver `this`
// references the same `this` made available from `declaration`. This is because
// is present and not a `this` or a singleton. For the `this` check, we don't need to actually compare if the dispatch
// receiver `this` references the same `this` made available from `declaration`. This is because
// 1. if `this` is not labeled, then it references the innermost `this` receiver. If the innermost scope is not the
// `declaration` body, then follow-up checks on following nodes would report there to be more instructions, which would
// then make this call non-tailrec.
@@ -93,9 +95,10 @@ object FirTailrecFunctionChecker : FirSimpleFunctionChecker() {
// is already bailed out earlier. So there is no need to report anything.
// c. `declaration` is a member function of an inner class and the receiver is a labeled `this` pointing to the outer
// class. The reasoning is the same with b.
// Also note that if the dispatch receiver is explicitly specify to be a singleton, the call is not compiled as a tailrec
// either. See KT-48602.
if (dispatchReceiver !is FirThisReceiverExpression && dispatchReceiver !is FirNoReceiverExpression) {
if (dispatchReceiver !is FirThisReceiverExpression &&
dispatchReceiver !is FirNoReceiverExpression &&
(declaration.dispatchReceiverType?.toSymbol(context.session) as? FirClassSymbol<*>)?.classKind?.isSingleton != true
) {
reporter.reportOn(functionCall.source, FirErrors.NON_TAIL_RECURSIVE_CALL, context)
return
}
@@ -14584,6 +14584,18 @@ public class FirBlackBoxCodegenTestGenerated extends AbstractFirBlackBoxCodegenT
runTest("compiler/testData/codegen/box/diagnostics/functions/tailRecursion/tailRecursionInFinally.kt");
}
@Test
@TestMetadata("tailrecWithExplicitCompanionObjectDispatcher.kt")
public void testTailrecWithExplicitCompanionObjectDispatcher() throws Exception {
runTest("compiler/testData/codegen/box/diagnostics/functions/tailRecursion/tailrecWithExplicitCompanionObjectDispatcher.kt");
}
@Test
@TestMetadata("tailrecWithExplicitObjectDispatcher.kt")
public void testTailrecWithExplicitObjectDispatcher() throws Exception {
runTest("compiler/testData/codegen/box/diagnostics/functions/tailRecursion/tailrecWithExplicitObjectDispatcher.kt");
}
@Test
@TestMetadata("thisReferences.kt")
public void testThisReferences() throws Exception {
@@ -16,11 +16,14 @@
package org.jetbrains.kotlin.backend.common
import org.jetbrains.kotlin.descriptors.ClassKind
import org.jetbrains.kotlin.ir.IrElement
import org.jetbrains.kotlin.ir.declarations.IrClass
import org.jetbrains.kotlin.ir.declarations.IrFunction
import org.jetbrains.kotlin.ir.declarations.IrSimpleFunction
import org.jetbrains.kotlin.ir.expressions.*
import org.jetbrains.kotlin.ir.types.IrSimpleType
import org.jetbrains.kotlin.ir.types.classOrNull
import org.jetbrains.kotlin.ir.types.isUnit
import org.jetbrains.kotlin.ir.util.usesDefaultArguments
import org.jetbrains.kotlin.ir.visitors.IrElementVisitor
@@ -107,6 +110,13 @@ fun collectTailRecursionCalls(irFunction: IrFunction): Set<IrCall> {
// Overridden functions using default arguments at tail call are not included: KT-4285
return
}
val dispatchReceiverType = irFunction.dispatchReceiverParameter?.type
if (dispatchReceiverType?.classOrNull?.owner?.kind?.isSingleton == true) {
// Dispatch receiver type is singleton and hence it can't be changed and the call must be tailrec.
result.add(expression)
return
}
expression.dispatchReceiver?.let {
if (it !is IrGetValue || it.symbol.owner != irFunction.dispatchReceiverParameter) {
@@ -0,0 +1,25 @@
// DONT_TARGET_EXACT_BACKEND: WASM
// WASM_MUTE_REASON: IGNORED_IN_JS
// IGNORE_BACKEND: JS_IR
// IGNORE_BACKEND: JS_IR_ES6
// DONT_RUN_GENERATED_CODE: JS
// IGNORE_BACKEND: JS
// IGNORE_BACKEND: JVM
// IGNORE_FIR_DIAGNOSTICS_DIFF
// Light analysis thinks this test passes but it doesn't because JVM backend does not compile this into a tailrec function.
// IGNORE_LIGHT_ANALYSIS
class C {
companion object {
<!NO_TAIL_CALLS_FOUND!>tailrec fun rec(i: Int)<!> {
if (i <= 0) return
C.<!NON_TAIL_RECURSIVE_CALL!>rec<!>(i - 1)
}
}
}
fun box(): String {
C.rec(100000)
return "OK"
}
@@ -0,0 +1,16 @@
// DONT_TARGET_EXACT_BACKEND: WASM
// WASM_MUTE_REASON: IGNORED_IN_JS
// IGNORE_BACKEND: JS_IR_ES6
// DONT_RUN_GENERATED_CODE: JS
object O {
tailrec fun rec(i: Int) {
if (i <= 0) return
O.rec(i - 1)
}
}
fun box(): String {
O.rec(100000)
return "OK"
}
@@ -7,8 +7,8 @@ object Foo {
this.foo2()
}
<!NO_TAIL_CALLS_FOUND!>tailrec fun foo3()<!> {
Foo.<!NON_TAIL_RECURSIVE_CALL!>foo3<!>()
tailrec fun foo3() {
Foo.foo3()
}
}
@@ -22,12 +22,12 @@ class Bar {
this.bar2()
}
<!NO_TAIL_CALLS_FOUND!>tailrec fun bar3()<!> {
Bar.<!NON_TAIL_RECURSIVE_CALL!>bar3<!>()
tailrec fun bar3() {
Bar.bar3()
}
<!NO_TAIL_CALLS_FOUND!>tailrec fun bar4()<!> {
Bar.Companion.<!NON_TAIL_RECURSIVE_CALL!>bar4<!>()
tailrec fun bar4() {
Bar.Companion.bar4()
}
}
}
@@ -1,16 +0,0 @@
object O {
// foo is the same, but the compiler currently doesn't compile this as tail recursive. See KT-48602
<!NO_TAIL_CALLS_FOUND!>tailrec fun foo(i: Int): Int<!> = if (i < 0) 0 else O.<!NON_TAIL_RECURSIVE_CALL!>foo<!>(i - 1)
}
class A {
<!NO_TAIL_CALLS_FOUND!>tailrec fun foo(i: Int)<!> = if (i < 0) 0 else A.foo(i - 1)
companion object {
fun foo(i: Int) = 42 + i
}
}
class B {
<!NO_TAIL_CALLS_FOUND!>tailrec fun foo(i: Int)<!> = if (i < 0) 0 else O.foo(i - 1)
}
@@ -1,5 +1,6 @@
// FIR_IDENTICAL
object O {
// foo is the same, but the compiler currently doesn't compile this as tail recursive. See KT-48602
// tailrec since `O` is a singleton
tailrec fun foo(i: Int): Int = if (i < 0) 0 else O.foo(i - 1)
}
@@ -14506,6 +14506,18 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest {
runTest("compiler/testData/codegen/box/diagnostics/functions/tailRecursion/tailRecursionInFinally.kt");
}
@Test
@TestMetadata("tailrecWithExplicitCompanionObjectDispatcher.kt")
public void testTailrecWithExplicitCompanionObjectDispatcher() throws Exception {
runTest("compiler/testData/codegen/box/diagnostics/functions/tailRecursion/tailrecWithExplicitCompanionObjectDispatcher.kt");
}
@Test
@TestMetadata("tailrecWithExplicitObjectDispatcher.kt")
public void testTailrecWithExplicitObjectDispatcher() throws Exception {
runTest("compiler/testData/codegen/box/diagnostics/functions/tailRecursion/tailrecWithExplicitObjectDispatcher.kt");
}
@Test
@TestMetadata("thisReferences.kt")
public void testThisReferences() throws Exception {
@@ -14584,6 +14584,18 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes
runTest("compiler/testData/codegen/box/diagnostics/functions/tailRecursion/tailRecursionInFinally.kt");
}
@Test
@TestMetadata("tailrecWithExplicitCompanionObjectDispatcher.kt")
public void testTailrecWithExplicitCompanionObjectDispatcher() throws Exception {
runTest("compiler/testData/codegen/box/diagnostics/functions/tailRecursion/tailrecWithExplicitCompanionObjectDispatcher.kt");
}
@Test
@TestMetadata("tailrecWithExplicitObjectDispatcher.kt")
public void testTailrecWithExplicitObjectDispatcher() throws Exception {
runTest("compiler/testData/codegen/box/diagnostics/functions/tailRecursion/tailrecWithExplicitObjectDispatcher.kt");
}
@Test
@TestMetadata("thisReferences.kt")
public void testThisReferences() throws Exception {
@@ -11684,6 +11684,11 @@ public class LightAnalysisModeTestGenerated extends AbstractLightAnalysisModeTes
runTest("compiler/testData/codegen/box/diagnostics/functions/tailRecursion/recursiveCallInInlineLambdaWithCapture.kt");
}
@TestMetadata("tailrecWithExplicitCompanionObjectDispatcher.kt")
public void ignoreTailrecWithExplicitCompanionObjectDispatcher() throws Exception {
runTest("compiler/testData/codegen/box/diagnostics/functions/tailRecursion/tailrecWithExplicitCompanionObjectDispatcher.kt");
}
private void runTest(String testDataFilePath) throws Exception {
KotlinTestUtils.runTest(this::doTest, TargetBackend.JVM, testDataFilePath);
}
@@ -11867,6 +11872,11 @@ public class LightAnalysisModeTestGenerated extends AbstractLightAnalysisModeTes
runTest("compiler/testData/codegen/box/diagnostics/functions/tailRecursion/tailRecursionInFinally.kt");
}
@TestMetadata("tailrecWithExplicitObjectDispatcher.kt")
public void testTailrecWithExplicitObjectDispatcher() throws Exception {
runTest("compiler/testData/codegen/box/diagnostics/functions/tailRecursion/tailrecWithExplicitObjectDispatcher.kt");
}
@TestMetadata("thisReferences.kt")
public void testThisReferences() throws Exception {
runTest("compiler/testData/codegen/box/diagnostics/functions/tailRecursion/thisReferences.kt");
@@ -10586,6 +10586,16 @@ public class IrJsCodegenBoxES6TestGenerated extends AbstractIrJsCodegenBoxES6Tes
runTest("compiler/testData/codegen/box/diagnostics/functions/tailRecursion/tailRecursionInFinally.kt");
}
@TestMetadata("tailrecWithExplicitCompanionObjectDispatcher.kt")
public void testTailrecWithExplicitCompanionObjectDispatcher() throws Exception {
runTest("compiler/testData/codegen/box/diagnostics/functions/tailRecursion/tailrecWithExplicitCompanionObjectDispatcher.kt");
}
@TestMetadata("tailrecWithExplicitObjectDispatcher.kt")
public void testTailrecWithExplicitObjectDispatcher() throws Exception {
runTest("compiler/testData/codegen/box/diagnostics/functions/tailRecursion/tailrecWithExplicitObjectDispatcher.kt");
}
@TestMetadata("thisReferences.kt")
public void testThisReferences() throws Exception {
runTest("compiler/testData/codegen/box/diagnostics/functions/tailRecursion/thisReferences.kt");
@@ -9992,6 +9992,16 @@ public class IrJsCodegenBoxTestGenerated extends AbstractIrJsCodegenBoxTest {
runTest("compiler/testData/codegen/box/diagnostics/functions/tailRecursion/tailRecursionInFinally.kt");
}
@TestMetadata("tailrecWithExplicitCompanionObjectDispatcher.kt")
public void testTailrecWithExplicitCompanionObjectDispatcher() throws Exception {
runTest("compiler/testData/codegen/box/diagnostics/functions/tailRecursion/tailrecWithExplicitCompanionObjectDispatcher.kt");
}
@TestMetadata("tailrecWithExplicitObjectDispatcher.kt")
public void testTailrecWithExplicitObjectDispatcher() throws Exception {
runTest("compiler/testData/codegen/box/diagnostics/functions/tailRecursion/tailrecWithExplicitObjectDispatcher.kt");
}
@TestMetadata("thisReferences.kt")
public void testThisReferences() throws Exception {
runTest("compiler/testData/codegen/box/diagnostics/functions/tailRecursion/thisReferences.kt");
@@ -9972,6 +9972,16 @@ public class JsCodegenBoxTestGenerated extends AbstractJsCodegenBoxTest {
runTest("compiler/testData/codegen/box/diagnostics/functions/tailRecursion/tailRecursionInFinally.kt");
}
@TestMetadata("tailrecWithExplicitCompanionObjectDispatcher.kt")
public void testTailrecWithExplicitCompanionObjectDispatcher() throws Exception {
runTest("compiler/testData/codegen/box/diagnostics/functions/tailRecursion/tailrecWithExplicitCompanionObjectDispatcher.kt");
}
@TestMetadata("tailrecWithExplicitObjectDispatcher.kt")
public void testTailrecWithExplicitObjectDispatcher() throws Exception {
runTest("compiler/testData/codegen/box/diagnostics/functions/tailRecursion/tailrecWithExplicitObjectDispatcher.kt");
}
@TestMetadata("thisReferences.kt")
public void testThisReferences() throws Exception {
runTest("compiler/testData/codegen/box/diagnostics/functions/tailRecursion/thisReferences.kt");