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.expressions.toResolvedCallableSymbol
|
||||||
import org.jetbrains.kotlin.fir.resolve.dfa.cfg.*
|
import org.jetbrains.kotlin.fir.resolve.dfa.cfg.*
|
||||||
import org.jetbrains.kotlin.fir.resolve.dfa.controlFlowGraph
|
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.symbols.impl.FirNamedFunctionSymbol
|
||||||
|
import org.jetbrains.kotlin.fir.types.toSymbol
|
||||||
|
|
||||||
object FirTailrecFunctionChecker : FirSimpleFunctionChecker() {
|
object FirTailrecFunctionChecker : FirSimpleFunctionChecker() {
|
||||||
override fun check(declaration: FirSimpleFunction, context: CheckerContext, reporter: DiagnosticReporter) {
|
override fun check(declaration: FirSimpleFunction, context: CheckerContext, reporter: DiagnosticReporter) {
|
||||||
@@ -80,8 +82,8 @@ object FirTailrecFunctionChecker : FirSimpleFunctionChecker() {
|
|||||||
}
|
}
|
||||||
val dispatchReceiver = functionCall.dispatchReceiver
|
val dispatchReceiver = functionCall.dispatchReceiver
|
||||||
// A tailrec call does not support changing dispatchers. Here we report changing dispatch receiver if the dispatch receiver
|
// 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`
|
// is present and not a `this` or a singleton. For the `this` check, we don't need to actually compare if the dispatch
|
||||||
// references the same `this` made available from `declaration`. This is because
|
// 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
|
// 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
|
// `declaration` body, then follow-up checks on following nodes would report there to be more instructions, which would
|
||||||
// then make this call non-tailrec.
|
// 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.
|
// 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
|
// 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.
|
// 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
|
if (dispatchReceiver !is FirThisReceiverExpression &&
|
||||||
// either. See KT-48602.
|
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)
|
reporter.reportOn(functionCall.source, FirErrors.NON_TAIL_RECURSIVE_CALL, context)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|||||||
+12
@@ -14584,6 +14584,18 @@ public class FirBlackBoxCodegenTestGenerated extends AbstractFirBlackBoxCodegenT
|
|||||||
runTest("compiler/testData/codegen/box/diagnostics/functions/tailRecursion/tailRecursionInFinally.kt");
|
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
|
@Test
|
||||||
@TestMetadata("thisReferences.kt")
|
@TestMetadata("thisReferences.kt")
|
||||||
public void testThisReferences() throws Exception {
|
public void testThisReferences() throws Exception {
|
||||||
|
|||||||
+10
@@ -16,11 +16,14 @@
|
|||||||
|
|
||||||
package org.jetbrains.kotlin.backend.common
|
package org.jetbrains.kotlin.backend.common
|
||||||
|
|
||||||
|
import org.jetbrains.kotlin.descriptors.ClassKind
|
||||||
import org.jetbrains.kotlin.ir.IrElement
|
import org.jetbrains.kotlin.ir.IrElement
|
||||||
import org.jetbrains.kotlin.ir.declarations.IrClass
|
import org.jetbrains.kotlin.ir.declarations.IrClass
|
||||||
import org.jetbrains.kotlin.ir.declarations.IrFunction
|
import org.jetbrains.kotlin.ir.declarations.IrFunction
|
||||||
import org.jetbrains.kotlin.ir.declarations.IrSimpleFunction
|
import org.jetbrains.kotlin.ir.declarations.IrSimpleFunction
|
||||||
import org.jetbrains.kotlin.ir.expressions.*
|
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.types.isUnit
|
||||||
import org.jetbrains.kotlin.ir.util.usesDefaultArguments
|
import org.jetbrains.kotlin.ir.util.usesDefaultArguments
|
||||||
import org.jetbrains.kotlin.ir.visitors.IrElementVisitor
|
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
|
// Overridden functions using default arguments at tail call are not included: KT-4285
|
||||||
return
|
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 {
|
expression.dispatchReceiver?.let {
|
||||||
if (it !is IrGetValue || it.symbol.owner != irFunction.dispatchReceiverParameter) {
|
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()
|
this.foo2()
|
||||||
}
|
}
|
||||||
|
|
||||||
<!NO_TAIL_CALLS_FOUND!>tailrec fun foo3()<!> {
|
tailrec fun foo3() {
|
||||||
Foo.<!NON_TAIL_RECURSIVE_CALL!>foo3<!>()
|
Foo.foo3()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -22,12 +22,12 @@ class Bar {
|
|||||||
this.bar2()
|
this.bar2()
|
||||||
}
|
}
|
||||||
|
|
||||||
<!NO_TAIL_CALLS_FOUND!>tailrec fun bar3()<!> {
|
tailrec fun bar3() {
|
||||||
Bar.<!NON_TAIL_RECURSIVE_CALL!>bar3<!>()
|
Bar.bar3()
|
||||||
}
|
}
|
||||||
|
|
||||||
<!NO_TAIL_CALLS_FOUND!>tailrec fun bar4()<!> {
|
tailrec fun bar4() {
|
||||||
Bar.Companion.<!NON_TAIL_RECURSIVE_CALL!>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 {
|
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)
|
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");
|
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
|
@Test
|
||||||
@TestMetadata("thisReferences.kt")
|
@TestMetadata("thisReferences.kt")
|
||||||
public void testThisReferences() throws Exception {
|
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");
|
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
|
@Test
|
||||||
@TestMetadata("thisReferences.kt")
|
@TestMetadata("thisReferences.kt")
|
||||||
public void testThisReferences() throws Exception {
|
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");
|
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 {
|
private void runTest(String testDataFilePath) throws Exception {
|
||||||
KotlinTestUtils.runTest(this::doTest, TargetBackend.JVM, testDataFilePath);
|
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");
|
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")
|
@TestMetadata("thisReferences.kt")
|
||||||
public void testThisReferences() throws Exception {
|
public void testThisReferences() throws Exception {
|
||||||
runTest("compiler/testData/codegen/box/diagnostics/functions/tailRecursion/thisReferences.kt");
|
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");
|
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")
|
@TestMetadata("thisReferences.kt")
|
||||||
public void testThisReferences() throws Exception {
|
public void testThisReferences() throws Exception {
|
||||||
runTest("compiler/testData/codegen/box/diagnostics/functions/tailRecursion/thisReferences.kt");
|
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");
|
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")
|
@TestMetadata("thisReferences.kt")
|
||||||
public void testThisReferences() throws Exception {
|
public void testThisReferences() throws Exception {
|
||||||
runTest("compiler/testData/codegen/box/diagnostics/functions/tailRecursion/thisReferences.kt");
|
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");
|
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")
|
@TestMetadata("thisReferences.kt")
|
||||||
public void testThisReferences() throws Exception {
|
public void testThisReferences() throws Exception {
|
||||||
runTest("compiler/testData/codegen/box/diagnostics/functions/tailRecursion/thisReferences.kt");
|
runTest("compiler/testData/codegen/box/diagnostics/functions/tailRecursion/thisReferences.kt");
|
||||||
|
|||||||
Reference in New Issue
Block a user