IR: make calls with explicit singleton receiver tailrec [KT-48602]
This commit is contained in:
committed by
TeamCityServer
parent
8525b4932b
commit
3c84fbcab1
+8
-5
@@ -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
|
||||
}
|
||||
|
||||
+12
@@ -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 {
|
||||
|
||||
+10
@@ -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) {
|
||||
|
||||
+25
@@ -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"
|
||||
}
|
||||
+16
@@ -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)
|
||||
}
|
||||
|
||||
|
||||
+12
@@ -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 {
|
||||
|
||||
+12
@@ -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 {
|
||||
|
||||
+10
@@ -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");
|
||||
|
||||
js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/es6/semantics/IrJsCodegenBoxES6TestGenerated.java
Generated
+10
@@ -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");
|
||||
|
||||
Generated
+10
@@ -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");
|
||||
|
||||
Generated
+10
@@ -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");
|
||||
|
||||
Reference in New Issue
Block a user