FIR Checker: check tailrec

Difference from FE1.0
* KT-4285: calls to virtual method with default argument should be
  reported as not tailrec. FE1.0 is missing such cases.
* KT-48600: calls inside lambda should be reported as not tailrec. FE1.0
  also misses such cases.
This commit is contained in:
Tianyu Geng
2021-09-02 11:00:45 -07:00
committed by TeamCityServer
parent 5df316a129
commit 8525b4932b
55 changed files with 876 additions and 216 deletions
@@ -5,7 +5,6 @@
// TODO: muted automatically, investigate should it be ran for JS or not
// DONT_RUN_GENERATED_CODE: JS
// IGNORE_BACKEND: JS
// IGNORE_FIR_DIAGNOSTICS_DIFF
<!NO_TAIL_CALLS_FOUND!>tailrec fun noTails()<!> {
// nothing here
@@ -7,7 +7,6 @@
// TODO: muted automatically, investigate should it be ran for JS or not
// DONT_RUN_GENERATED_CODE: JS
// IGNORE_BACKEND: JS
// IGNORE_FIR_DIAGNOSTICS_DIFF
class B {
inner class C {
@@ -5,7 +5,6 @@
// TODO: muted automatically, investigate should it be ran for JS or not
// DONT_RUN_GENERATED_CODE: JS
// IGNORE_BACKEND: JS
// IGNORE_FIR_DIAGNOSTICS_DIFF
tailrec fun test(x : Int) : Int {
var z = if (x > 3) 3 else x
@@ -5,7 +5,6 @@
// TODO: muted automatically, investigate should it be ran for JS or not
// DONT_RUN_GENERATED_CODE: JS
// IGNORE_BACKEND: JS
// IGNORE_FIR_DIAGNOSTICS_DIFF
tailrec fun test(x : Int) : Int {
if (x == 1) {
@@ -1,5 +1,6 @@
// KT-16549
// IGNORE_BACKEND: JVM
// IGNORE_FIR_DIAGNOSTICS_DIFF
class TailInline {
private inline fun act(action: () -> Unit) {
@@ -1,6 +1,7 @@
// KT-14961
// IGNORE_BACKEND: JVM, JS_IR, WASM
// WITH_RUNTIME
// IGNORE_FIR_DIAGNOSTICS_DIFF
fun listOfFactor(number: Int): List<Int> {
tailrec fun listOfFactor(number: Int, acc: List<Int>): List<Int> {
@@ -7,7 +7,6 @@
// TODO: muted automatically, investigate should it be ran for JS or not
// DONT_RUN_GENERATED_CODE: JS
// IGNORE_BACKEND: JS
// IGNORE_FIR_DIAGNOSTICS_DIFF
<!NO_TAIL_CALLS_FOUND!>tailrec fun foo()<!> {
bar {
@@ -5,7 +5,6 @@
// TODO: muted automatically, investigate should it be ran for JS or not
// DONT_RUN_GENERATED_CODE: JS
// IGNORE_BACKEND: JS
// IGNORE_FIR_DIAGNOSTICS_DIFF
<!NO_TAIL_CALLS_FOUND!>tailrec fun foo()<!> {
fun bar() {
@@ -5,7 +5,6 @@
// TODO: muted automatically, investigate should it be ran for JS or not
// DONT_RUN_GENERATED_CODE: JS
// IGNORE_BACKEND: JS
// IGNORE_FIR_DIAGNOSTICS_DIFF
tailrec fun test(x : Int) : Int {
return if (x == 1) {
@@ -5,7 +5,6 @@
// TODO: muted automatically, investigate should it be ran for JS or not
// DONT_RUN_GENERATED_CODE: JS
// IGNORE_BACKEND: JS
// IGNORE_FIR_DIAGNOSTICS_DIFF
<!NO_TAIL_CALLS_FOUND!>tailrec fun test(counter : Int) : Int<!> {
if (counter == 0) return 0
@@ -5,7 +5,6 @@
// TODO: muted automatically, investigate should it be ran for JS or not
// DONT_RUN_GENERATED_CODE: JS
// IGNORE_BACKEND: JS
// IGNORE_FIR_DIAGNOSTICS_DIFF
<!NO_TAIL_CALLS_FOUND!>tailrec fun test(counter : Int) : Int<!> {
if (counter == 0) return 0
@@ -5,7 +5,6 @@
// TODO: muted automatically, investigate should it be ran for JS or not
// DONT_RUN_GENERATED_CODE: JS
// IGNORE_BACKEND: JS
// IGNORE_FIR_DIAGNOSTICS_DIFF
<!NO_TAIL_CALLS_FOUND!>tailrec fun test(counter : Int) : Int<!> {
if (counter == 0) return 0
@@ -5,7 +5,6 @@
// TODO: muted automatically, investigate should it be ran for JS or not
// DONT_RUN_GENERATED_CODE: JS
// IGNORE_BACKEND: JS
// IGNORE_FIR_DIAGNOSTICS_DIFF
<!NO_TAIL_CALLS_FOUND!>tailrec fun test(counter : Int) : Int<!> {
if (counter == 0) return 0
@@ -5,7 +5,6 @@
// TODO: muted automatically, investigate should it be ran for JS or not
// DONT_RUN_GENERATED_CODE: JS
// IGNORE_BACKEND: JS
// IGNORE_FIR_DIAGNOSTICS_DIFF
tailrec fun test(x : Int) : Int =
if (x == 1) {
@@ -5,7 +5,6 @@
// TODO: muted automatically, investigate should it be ran for JS or not
// DONT_RUN_GENERATED_CODE: JS
// IGNORE_BACKEND: JS
// IGNORE_FIR_DIAGNOSTICS_DIFF
tailrec fun test(x : Int) : Int {
if (x == 10) {
@@ -5,7 +5,6 @@
// TODO: muted automatically, investigate should it be ran for JS or not
// DONT_RUN_GENERATED_CODE: JS
// IGNORE_BACKEND: JS
// IGNORE_FIR_DIAGNOSTICS_DIFF
tailrec fun test(x : Int) : Int {
if (x == 0) {
@@ -5,7 +5,6 @@
// TODO: muted automatically, investigate should it be ran for JS or not
// DONT_RUN_GENERATED_CODE: JS
// IGNORE_BACKEND: JS
// IGNORE_FIR_DIAGNOSTICS_DIFF
<!NO_TAIL_CALLS_FOUND!>tailrec fun test(go: Boolean) : Unit<!> {
if (!go) return
@@ -5,7 +5,6 @@
// TODO: muted automatically, investigate should it be ran for JS or not
// DONT_RUN_GENERATED_CODE: JS
// IGNORE_BACKEND: JS
// IGNORE_FIR_DIAGNOSTICS_DIFF
class A {
tailrec fun f1(c : Int) {
@@ -5,7 +5,6 @@
// TODO: muted automatically, investigate should it be ran for JS or not
// DONT_RUN_GENERATED_CODE: JS
// IGNORE_BACKEND: JS
// IGNORE_FIR_DIAGNOSTICS_DIFF
tailrec fun test(x : Int) : Unit {
if (x == 1) {
@@ -5,7 +5,6 @@
// TODO: muted automatically, investigate should it be ran for JS or not
// DONT_RUN_GENERATED_CODE: JS
// IGNORE_BACKEND: JS
// IGNORE_FIR_DIAGNOSTICS_DIFF
tailrec fun withWhen(counter : Int) : Int =
when (counter) {
@@ -7,7 +7,6 @@
// TODO: muted automatically, investigate should it be ran for JS or not
// DONT_RUN_GENERATED_CODE: JS
// IGNORE_BACKEND: JS
// IGNORE_FIR_DIAGNOSTICS_DIFF
tailrec fun withWhen(counter : Int, d : Any) : Int =
when (counter) {
@@ -5,7 +5,6 @@
// TODO: muted automatically, investigate should it be ran for JS or not
// DONT_RUN_GENERATED_CODE: JS
// IGNORE_BACKEND: JS
// IGNORE_FIR_DIAGNOSTICS_DIFF
tailrec fun withWhen2(counter : Int) : Int =
when {
+80
View File
@@ -0,0 +1,80 @@
// FIR_IDENTICAL
// WITH_STDLIB
// !DIAGNOSTICS: -UNREACHABLE_CODE
<!NO_TAIL_CALLS_FOUND!>tailrec fun foo1()<!> {
<!NON_TAIL_RECURSIVE_CALL!>foo1<!>()
1
}
<!NO_TAIL_CALLS_FOUND!>tailrec fun foo2()<!> {
<!NON_TAIL_RECURSIVE_CALL!>foo2<!>()
val i = 1
}
<!NO_TAIL_CALLS_FOUND!>tailrec fun foo3()<!> {
<!NON_TAIL_RECURSIVE_CALL!>foo3<!>()
foo1()
}
tailrec fun foo4() {
if (true) foo4()
else foo3()
}
tailrec fun foo5() {
return foo5()
foo4()
}
<!NO_TAIL_CALLS_FOUND!>tailrec fun foo6(b: Boolean)<!> {
while (b) {
<!NON_TAIL_RECURSIVE_CALL!>foo6<!>(!b)
}
}
tailrec fun foo7_return() {
while (true) {
foo7_return()
return
}
}
<!NO_TAIL_CALLS_FOUND!>tailrec fun foo7_break()<!> {
while (true) {
<!NON_TAIL_RECURSIVE_CALL!>foo7_break<!>()
break
}
}
<!NO_TAIL_CALLS_FOUND!>tailrec fun foo7_continue()<!> {
while (true) {
<!NON_TAIL_RECURSIVE_CALL!>foo7_continue<!>()
continue
}
}
<!NO_TAIL_CALLS_FOUND!>tailrec fun foo8()<!> {
while (true) {
<!NON_TAIL_RECURSIVE_CALL!>foo8<!>()
throw Exception()
}
foo8()
}
<!NO_TAIL_CALLS_FOUND!>tailrec fun foo9()<!> {
<!NON_TAIL_RECURSIVE_CALL!>foo9<!>()
fun bar() {}
}
<!NO_TAIL_CALLS_FOUND!>tailrec fun foo10()<!> {
<!NON_TAIL_RECURSIVE_CALL!>foo10<!>()
class Bar {
val i = 1
}
}
<!NO_TAIL_CALLS_FOUND!>tailrec fun foo11(): String<!> {
return "hello ${<!NON_TAIL_RECURSIVE_CALL!>foo11<!>()}"
}
+16
View File
@@ -0,0 +1,16 @@
package
public tailrec fun foo1(): kotlin.Unit
public tailrec fun foo10(): kotlin.Unit
public tailrec fun foo11(): kotlin.String
public tailrec fun foo2(): kotlin.Unit
public tailrec fun foo3(): kotlin.Unit
public tailrec fun foo4(): kotlin.Unit
public tailrec fun foo5(): kotlin.Unit
public tailrec fun foo6(/*0*/ b: kotlin.Boolean): kotlin.Unit
public tailrec fun foo7_break(): kotlin.Unit
public tailrec fun foo7_continue(): kotlin.Unit
public tailrec fun foo7_return(): kotlin.Unit
public tailrec fun foo8(): kotlin.Unit
public tailrec fun foo9(): kotlin.Unit
@@ -0,0 +1,37 @@
// WITH_STDLIB
<!NO_TAIL_CALLS_FOUND!>tailrec fun foo1()<!> {
run {
<!NON_TAIL_RECURSIVE_CALL!>foo1<!>()
}
}
fun myRun(f: () -> Unit) = f()
<!NO_TAIL_CALLS_FOUND!>tailrec fun foo2()<!> {
myRun {
<!NON_TAIL_RECURSIVE_CALL!>foo2<!>()
}
}
<!NO_TAIL_CALLS_FOUND!>tailrec fun foo3()<!> {
fun bar() {
<!NON_TAIL_RECURSIVE_CALL!>foo3<!>()
}
bar()
}
class A {
<!NO_TAIL_CALLS_FOUND!>tailrec fun foo4()<!> {
with(this) {
<!NON_TAIL_RECURSIVE_CALL!>foo4<!>()
}
}
}
<!NO_TAIL_CALLS_FOUND!>tailrec fun foo5()<!> {
run {
return <!NON_TAIL_RECURSIVE_CALL!>foo5<!>()
}
}
@@ -0,0 +1,37 @@
// WITH_STDLIB
<!NO_TAIL_CALLS_FOUND!>tailrec fun foo1()<!> {
run {
<!NON_TAIL_RECURSIVE_CALL!>foo1<!>()
}
}
fun myRun(f: () -> Unit) = f()
<!NO_TAIL_CALLS_FOUND!>tailrec fun foo2()<!> {
myRun {
<!NON_TAIL_RECURSIVE_CALL!>foo2<!>()
}
}
<!NO_TAIL_CALLS_FOUND!>tailrec fun foo3()<!> {
fun bar() {
<!NON_TAIL_RECURSIVE_CALL!>foo3<!>()
}
bar()
}
class A {
<!NO_TAIL_CALLS_FOUND!>tailrec fun foo4()<!> {
with(this) {
<!NON_TAIL_RECURSIVE_CALL!>foo4<!>()
}
}
}
tailrec fun foo5() {
run {
return foo5()
}
}
@@ -0,0 +1,16 @@
package
public tailrec fun foo1(): kotlin.Unit
public tailrec fun foo2(): kotlin.Unit
public tailrec fun foo3(): kotlin.Unit
public tailrec fun foo5(): kotlin.Unit
public fun myRun(/*0*/ f: () -> kotlin.Unit): kotlin.Unit
public final class A {
public constructor A()
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public final tailrec fun foo4(): kotlin.Unit
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
}
+64
View File
@@ -0,0 +1,64 @@
// FIR_IDENTICAL
<!NO_TAIL_CALLS_FOUND!>tailrec fun foo1()<!> {
try {
<!TAIL_RECURSION_IN_TRY_IS_NOT_SUPPORTED!>foo1<!>()
} catch (e: Exception) {
<!TAIL_RECURSION_IN_TRY_IS_NOT_SUPPORTED!>foo1<!>()
} finally {
<!TAIL_RECURSION_IN_TRY_IS_NOT_SUPPORTED!>foo1<!>()
}
}
<!NO_TAIL_CALLS_FOUND!>tailrec fun foo2()<!> {
try {
<!TAIL_RECURSION_IN_TRY_IS_NOT_SUPPORTED!>foo2<!>()
foo1()
} catch (e: Exception) {
<!TAIL_RECURSION_IN_TRY_IS_NOT_SUPPORTED!>foo2<!>()
foo1()
} finally {
<!TAIL_RECURSION_IN_TRY_IS_NOT_SUPPORTED!>foo2<!>()
foo1()
}
}
<!NO_TAIL_CALLS_FOUND!>tailrec fun foo3()<!> {
try {
try {
<!TAIL_RECURSION_IN_TRY_IS_NOT_SUPPORTED!>foo3<!>()
} finally {
}
} catch (e: Exception) {
try {
<!TAIL_RECURSION_IN_TRY_IS_NOT_SUPPORTED!>foo3<!>()
} finally {
}
} finally {
try {
<!TAIL_RECURSION_IN_TRY_IS_NOT_SUPPORTED!>foo3<!>()
} finally {
}
}
}
<!NO_TAIL_CALLS_FOUND!>tailrec fun foo4()<!> {
try {
if (true) {
<!TAIL_RECURSION_IN_TRY_IS_NOT_SUPPORTED!>foo4<!>()
} else {
foo1()
}
} catch (e: Exception) {
if (true) {
<!TAIL_RECURSION_IN_TRY_IS_NOT_SUPPORTED!>foo4<!>()
} else {
foo1()
}
} finally {
if (true) {
<!TAIL_RECURSION_IN_TRY_IS_NOT_SUPPORTED!>foo4<!>()
} else {
foo1()
}
}
}
+7
View File
@@ -0,0 +1,7 @@
package
public tailrec fun foo1(): kotlin.Unit
public tailrec fun foo2(): kotlin.Unit
public tailrec fun foo3(): kotlin.Unit
public tailrec fun foo4(): kotlin.Unit
@@ -1,15 +1,15 @@
//!LANGUAGE: -ProhibitTailrecOnVirtualMember
open class A {
tailrec open fun foo(x: Int) {
<!TAILREC_ON_VIRTUAL_MEMBER_ERROR!>tailrec open fun foo(x: Int)<!> {
foo(x)
}
internal tailrec open fun bar(y: Int) {
<!TAILREC_ON_VIRTUAL_MEMBER_ERROR!>internal tailrec open fun bar(y: Int)<!> {
bar(y)
}
protected tailrec open fun baz(y: Int) {
<!TAILREC_ON_VIRTUAL_MEMBER_ERROR!>protected tailrec open fun baz(y: Int)<!> {
baz(y)
}
@@ -38,15 +38,15 @@ open class B : A() {
open class C : A() {
tailrec override fun foo(x: Int) {
<!TAILREC_ON_VIRTUAL_MEMBER_ERROR!>tailrec override fun foo(x: Int)<!> {
foo(x)
}
tailrec override fun bar(y: Int) {
<!TAILREC_ON_VIRTUAL_MEMBER_ERROR!>tailrec override fun bar(y: Int)<!> {
bar(y)
}
tailrec override fun baz(y: Int) {
<!TAILREC_ON_VIRTUAL_MEMBER_ERROR!>tailrec override fun baz(y: Int)<!> {
baz(y)
}
}
@@ -66,15 +66,15 @@ object D : A() {
}
sealed class E : A() {
tailrec override fun foo(x: Int) {
<!TAILREC_ON_VIRTUAL_MEMBER_ERROR!>tailrec override fun foo(x: Int)<!> {
foo(x)
}
tailrec override fun bar(y: Int) {
<!TAILREC_ON_VIRTUAL_MEMBER_ERROR!>tailrec override fun bar(y: Int)<!> {
bar(y)
}
tailrec override fun baz(y: Int) {
<!TAILREC_ON_VIRTUAL_MEMBER_ERROR!>tailrec override fun baz(y: Int)<!> {
baz(y)
}
@@ -109,15 +109,15 @@ enum class F {
}
};
tailrec open fun foo(x: Int) {
<!TAILREC_ON_VIRTUAL_MEMBER_ERROR!>tailrec open fun foo(x: Int)<!> {
foo(x)
}
internal tailrec open fun bar(y: Int) {
<!TAILREC_ON_VIRTUAL_MEMBER_ERROR!>internal tailrec open fun bar(y: Int)<!> {
bar(y)
}
protected tailrec open fun baz(y: Int) {
<!TAILREC_ON_VIRTUAL_MEMBER_ERROR!>protected tailrec open fun baz(y: Int)<!> {
baz(y)
}
@@ -134,15 +134,15 @@ enum class G {
G1;
tailrec open fun foo(x: Int) {
<!TAILREC_ON_VIRTUAL_MEMBER_ERROR!>tailrec open fun foo(x: Int)<!> {
foo(x)
}
internal tailrec open fun bar(y: Int) {
<!TAILREC_ON_VIRTUAL_MEMBER_ERROR!>internal tailrec open fun bar(y: Int)<!> {
bar(y)
}
protected tailrec open fun baz(y: Int) {
<!TAILREC_ON_VIRTUAL_MEMBER_ERROR!>protected tailrec open fun baz(y: Int)<!> {
baz(y)
}
@@ -1,170 +0,0 @@
//!LANGUAGE: +ProhibitTailrecOnVirtualMember
open class A {
tailrec open fun foo(x: Int) {
foo(x)
}
internal tailrec open fun bar(y: Int) {
bar(y)
}
protected tailrec open fun baz(y: Int) {
baz(y)
}
private tailrec fun boo(y: Int) {
boo(y)
}
internal tailrec fun baa(y: Int) {
baa(y)
}
}
open class B : A() {
final tailrec override fun foo(x: Int) {
foo(x)
}
final tailrec override fun bar(y: Int) {
bar(y)
}
final tailrec override fun baz(y: Int) {
baz(y)
}
}
open class C : A() {
tailrec override fun foo(x: Int) {
foo(x)
}
tailrec override fun bar(y: Int) {
bar(y)
}
tailrec override fun baz(y: Int) {
baz(y)
}
}
object D : A() {
tailrec override fun foo(x: Int) {
foo(x)
}
tailrec override fun bar(y: Int) {
bar(y - 1)
}
tailrec override fun baz(y: Int) {
baz(y)
}
}
sealed class E : A() {
tailrec override fun foo(x: Int) {
foo(x)
}
tailrec override fun bar(y: Int) {
bar(y)
}
tailrec override fun baz(y: Int) {
baz(y)
}
class E1 : E() {
tailrec override fun foo(x: Int) {
foo(x)
}
tailrec override fun bar(y: Int) {
bar(y)
}
tailrec override fun baz(y: Int) {
baz(y)
}
}
}
enum class F {
F0,
F1() {
tailrec override fun foo(x: Int) {
foo(x)
}
tailrec override fun bar(y: Int) {
bar(y)
}
tailrec override fun baz(y: Int) {
baz(y)
}
};
tailrec open fun foo(x: Int) {
foo(x)
}
internal tailrec open fun bar(y: Int) {
bar(y)
}
protected tailrec open fun baz(y: Int) {
baz(y)
}
private tailrec fun boo(y: Int) {
boo(y)
}
internal tailrec fun baa(y: Int) {
baa(y)
}
}
enum class G {
G1;
tailrec open fun foo(x: Int) {
foo(x)
}
internal tailrec open fun bar(y: Int) {
bar(y)
}
protected tailrec open fun baz(y: Int) {
baz(y)
}
private tailrec fun boo(y: Int) {
boo(y)
}
internal tailrec fun baa(y: Int) {
baa(y)
}
}
val z = object : A() {
tailrec override fun foo(x: Int) {
foo(x)
}
tailrec override fun bar(y: Int) {
bar(y)
}
tailrec override fun baz(y: Int) {
baz(y)
}
}
@@ -1,3 +1,4 @@
// FIR_IDENTICAL
//!LANGUAGE: +ProhibitTailrecOnVirtualMember
open class A {
@@ -6,12 +6,12 @@ open class A {
}
class B: A() {
tailrec override fun foo(x: Int) {
foo()
<!NO_TAIL_CALLS_FOUND!>tailrec override fun foo(x: Int)<!> {
<!NON_TAIL_RECURSIVE_CALL!>foo<!>()
}
tailrec override fun gav(y: Int, z: Int) {
gav(y)
<!NO_TAIL_CALLS_FOUND!>tailrec override fun gav(y: Int, z: Int)<!> {
<!NON_TAIL_RECURSIVE_CALL!>gav<!>(y)
}
tailrec fun bar(y: Double): Double = bar(y * 2.0)
@@ -29,4 +29,4 @@ class C: A() {
tailrec fun bar(y: Int = 1, z: Int = 2) {
bar(z)
}
}
}
@@ -0,0 +1,53 @@
object Foo {
tailrec fun foo1() {
foo1()
}
tailrec fun foo2() {
this.foo2()
}
<!NO_TAIL_CALLS_FOUND!>tailrec fun foo3()<!> {
Foo.<!NON_TAIL_RECURSIVE_CALL!>foo3<!>()
}
}
class Bar {
companion object {
tailrec fun bar1() {
bar1()
}
tailrec fun bar2() {
this.bar2()
}
<!NO_TAIL_CALLS_FOUND!>tailrec fun bar3()<!> {
Bar.<!NON_TAIL_RECURSIVE_CALL!>bar3<!>()
}
<!NO_TAIL_CALLS_FOUND!>tailrec fun bar4()<!> {
Bar.Companion.<!NON_TAIL_RECURSIVE_CALL!>bar4<!>()
}
}
}
enum class E {
A {
override tailrec fun rec() {
rec()
}
},
B {
override tailrec fun rec() {
this.rec()
}
},
C {
<!NO_TAIL_CALLS_FOUND!>override tailrec fun rec()<!> {
C.rec() // resolution goes to `E.rec`. Hence the resolved symbol is considered different from `C.rec`.
}
};
abstract fun rec()
}
+53
View File
@@ -0,0 +1,53 @@
object Foo {
tailrec fun foo1() {
foo1()
}
tailrec fun foo2() {
this.foo2()
}
tailrec fun foo3() {
Foo.foo3()
}
}
class Bar {
companion object {
tailrec fun bar1() {
bar1()
}
tailrec fun bar2() {
this.bar2()
}
<!NO_TAIL_CALLS_FOUND!>tailrec fun bar3()<!> {
Bar.<!NON_TAIL_RECURSIVE_CALL!>bar3<!>()
}
tailrec fun bar4() {
Bar.Companion.bar4()
}
}
}
enum class E {
A {
override tailrec fun rec() {
rec()
}
},
B {
override tailrec fun rec() {
this.rec()
}
},
C {
<!NO_TAIL_CALLS_FOUND!>override tailrec fun rec()<!> {
C.rec() // resolution goes to `E.rec`. Hence the resolved symbol is considered different from `C.rec`.
}
};
abstract fun rec()
}
@@ -0,0 +1,54 @@
package
public final class Bar {
public constructor Bar()
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
public companion object Companion {
private constructor Companion()
public final tailrec fun bar1(): kotlin.Unit
public final tailrec fun bar2(): kotlin.Unit
public final tailrec fun bar3(): kotlin.Unit
public final tailrec fun bar4(): kotlin.Unit
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
}
}
public final enum class E : kotlin.Enum<E> {
enum entry A
enum entry B
enum entry C
private constructor E()
public final override /*1*/ /*fake_override*/ val name: kotlin.String
public final override /*1*/ /*fake_override*/ val ordinal: kotlin.Int
protected final override /*1*/ /*fake_override*/ fun clone(): kotlin.Any
public final override /*1*/ /*fake_override*/ fun compareTo(/*0*/ other: E): kotlin.Int
public final override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
protected/*protected and package*/ final override /*1*/ /*fake_override*/ /*isHiddenForResolutionEverywhereBesideSupercalls*/ fun finalize(): kotlin.Unit
public final override /*1*/ /*fake_override*/ /*isHiddenForResolutionEverywhereBesideSupercalls*/ fun getDeclaringClass(): java.lang.Class<E!>!
public final override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public abstract fun rec(): kotlin.Unit
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
// Static members
public final /*synthesized*/ fun valueOf(/*0*/ value: kotlin.String): E
public final /*synthesized*/ fun values(): kotlin.Array<E>
}
public object Foo {
private constructor Foo()
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public final tailrec fun foo1(): kotlin.Unit
public final tailrec fun foo2(): kotlin.Unit
public final tailrec fun foo3(): kotlin.Unit
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
}
@@ -0,0 +1,21 @@
// FIR_IDENTICAL
class A(val a: A) {
<!NO_TAIL_CALLS_FOUND!>tailrec fun foo1()<!> {
a.<!NON_TAIL_RECURSIVE_CALL!>foo1<!>()
}
tailrec fun foo2() {
this.foo2()
}
tailrec fun foo3() {
foo3()
}
<!NO_TAIL_CALLS_FOUND!>tailrec fun foo4()<!> {
with(a) {
<!NON_TAIL_RECURSIVE_CALL!>foo4<!>()
return
}
}
}
@@ -0,0 +1,14 @@
package
public final class A {
public constructor A(/*0*/ a: A)
public final val a: A
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public final tailrec fun foo1(): kotlin.Unit
public final tailrec fun foo2(): kotlin.Unit
public final tailrec fun foo3(): kotlin.Unit
public final tailrec fun foo4(): kotlin.Unit
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
}
@@ -0,0 +1,14 @@
// FIR_IDENTICAL
tailrec fun String.foo1() {
"".foo1()
}
tailrec fun String.foo2() {
this.foo2()
}
<!NO_TAIL_CALLS_FOUND!>tailrec fun String.foo3()<!> {
with(this) {
<!NON_TAIL_RECURSIVE_CALL!>foo3<!>()
}
}
@@ -0,0 +1,5 @@
package
public tailrec fun kotlin.String.foo1(): kotlin.Unit
public tailrec fun kotlin.String.foo2(): kotlin.Unit
public tailrec fun kotlin.String.foo3(): kotlin.Unit
@@ -1,10 +1,10 @@
object O {
// This is correct, foo is the same
tailrec fun foo(i: Int): Int = if (i < 0) 0 else O.foo(i - 1)
// 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 {
tailrec fun foo(i: Int) = if (i < 0) 0 else A.foo(i - 1)
<!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
@@ -12,5 +12,5 @@ class A {
}
class B {
tailrec fun foo(i: Int) = if (i < 0) 0 else O.foo(i - 1)
}
<!NO_TAIL_CALLS_FOUND!>tailrec fun foo(i: Int)<!> = if (i < 0) 0 else O.foo(i - 1)
}
@@ -1,5 +1,5 @@
object O {
// This is correct, foo is the same
// foo is the same, but the compiler currently doesn't compile this as tail recursive. See KT-48602
tailrec fun foo(i: Int): Int = if (i < 0) 0 else O.foo(i - 1)
}