From 4070f13e261fe7df9e35f11a3f6216f642d3e1c8 Mon Sep 17 00:00:00 2001 From: Ilmir Usmanov Date: Tue, 27 Mar 2018 15:49:09 +0300 Subject: [PATCH] Do not generate beforeInlineMarker on tailrec jump Before this change, the codegen used to generate beforeInlineMarker in order to spill variables later. However, since the tailrec call is replaced with jump, no afterInlineMarker is generated, leading to inconsistency of these markers. Thus, the spilling fails. This change disables generating of beforeInlineMarker. #KT-21521: Fixed --- .../kotlin/codegen/ExpressionCodegen.java | 2 +- .../tailrec/controlFlowIf.kt | 41 +++++++++ .../tailrec/controlFlowWhen.kt | 41 +++++++++ .../{tailrec.kt => tailrec/extention.kt} | 0 .../featureIntersection/tailrec/infixCall.kt | 25 ++++++ .../tailrec/infixRecursiveCall.kt | 23 +++++ .../tailrec/realIteratorFoldl.kt | 24 +++++ .../tailrec/realStringEscape.kt | 27 ++++++ .../tailrec/realStringRepeat.kt | 21 +++++ .../tailrec/returnInParentheses.kt | 21 +++++ .../featureIntersection/tailrec/sum.kt | 23 +++++ .../tailrec/tailCallInBlockInParentheses.kt | 23 +++++ .../tailrec/tailCallInParentheses.kt | 21 +++++ .../featureIntersection/tailrec/whenWithIs.kt | 31 +++++++ .../ir/IrBlackBoxCodegenTestGenerated.java | 89 ++++++++++++++++++- .../codegen/BlackBoxCodegenTestGenerated.java | 89 ++++++++++++++++++- .../LightAnalysisModeTestGenerated.java | 89 ++++++++++++++++++- .../semantics/JsCodegenBoxTestGenerated.java | 89 ++++++++++++++++++- 18 files changed, 662 insertions(+), 17 deletions(-) create mode 100644 compiler/testData/codegen/box/coroutines/featureIntersection/tailrec/controlFlowIf.kt create mode 100644 compiler/testData/codegen/box/coroutines/featureIntersection/tailrec/controlFlowWhen.kt rename compiler/testData/codegen/box/coroutines/featureIntersection/{tailrec.kt => tailrec/extention.kt} (100%) create mode 100644 compiler/testData/codegen/box/coroutines/featureIntersection/tailrec/infixCall.kt create mode 100644 compiler/testData/codegen/box/coroutines/featureIntersection/tailrec/infixRecursiveCall.kt create mode 100644 compiler/testData/codegen/box/coroutines/featureIntersection/tailrec/realIteratorFoldl.kt create mode 100644 compiler/testData/codegen/box/coroutines/featureIntersection/tailrec/realStringEscape.kt create mode 100644 compiler/testData/codegen/box/coroutines/featureIntersection/tailrec/realStringRepeat.kt create mode 100644 compiler/testData/codegen/box/coroutines/featureIntersection/tailrec/returnInParentheses.kt create mode 100644 compiler/testData/codegen/box/coroutines/featureIntersection/tailrec/sum.kt create mode 100644 compiler/testData/codegen/box/coroutines/featureIntersection/tailrec/tailCallInBlockInParentheses.kt create mode 100644 compiler/testData/codegen/box/coroutines/featureIntersection/tailrec/tailCallInParentheses.kt create mode 100644 compiler/testData/codegen/box/coroutines/featureIntersection/tailrec/whenWithIs.kt diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/ExpressionCodegen.java b/compiler/backend/src/org/jetbrains/kotlin/codegen/ExpressionCodegen.java index fb85908e14c..612066da86b 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/ExpressionCodegen.java +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/ExpressionCodegen.java @@ -2416,7 +2416,7 @@ public class ExpressionCodegen extends KtVisitor impleme ) { boolean isSafeCallOrOnStack = receiver instanceof StackValue.SafeCall || receiver instanceof StackValue.OnStack; - if (isSuspendCall && !isSafeCallOrOnStack) { + if (isSuspendCall && !isSafeCallOrOnStack && !tailRecursionCodegen.isTailRecursion(resolvedCall)) { // Inline markers are used to spill the stack before coroutine suspension addInlineMarker(v, true); } diff --git a/compiler/testData/codegen/box/coroutines/featureIntersection/tailrec/controlFlowIf.kt b/compiler/testData/codegen/box/coroutines/featureIntersection/tailrec/controlFlowIf.kt new file mode 100644 index 00000000000..07e9f23fcf2 --- /dev/null +++ b/compiler/testData/codegen/box/coroutines/featureIntersection/tailrec/controlFlowIf.kt @@ -0,0 +1,41 @@ +// WITH_RUNTIME +// WITH_COROUTINES + +import helpers.* +import kotlin.coroutines.experimental.* + +class CompilerKillingIterator(private val underlying: Iterator, private val transform: suspend (e: T) -> Iterator) { + private var currentIt: Iterator = object : Iterator { + override fun hasNext() = false + + override fun next(): R = null!! + } + + suspend tailrec fun next(): R { + return if (currentIt.hasNext()) { + currentIt.next() + } else if (underlying.hasNext()) { + currentIt = transform(underlying.next()) + next() + } else { + throw IllegalArgumentException("Cannot call next() on the empty iterator") + } + } + + suspend fun hasNext() = currentIt.hasNext() || underlying.hasNext() +} + +fun builder(c: suspend () -> Unit) { + c.startCoroutine(EmptyContinuation) +} + +fun box(): String { + var res = "" + builder { + val iter = CompilerKillingIterator("ok".asIterable().iterator()) { ("" + it.toUpperCase()).asIterable().iterator() } + while (iter.hasNext()) { + res += iter.next() + } + } + return res +} \ No newline at end of file diff --git a/compiler/testData/codegen/box/coroutines/featureIntersection/tailrec/controlFlowWhen.kt b/compiler/testData/codegen/box/coroutines/featureIntersection/tailrec/controlFlowWhen.kt new file mode 100644 index 00000000000..ade0b26af41 --- /dev/null +++ b/compiler/testData/codegen/box/coroutines/featureIntersection/tailrec/controlFlowWhen.kt @@ -0,0 +1,41 @@ +// WITH_RUNTIME +// WITH_COROUTINES + +import helpers.* +import kotlin.coroutines.experimental.* + +class CompilerKillingIterator(private val underlying: Iterator, private val transform: suspend (e: T) -> Iterator) { + private var currentIt: Iterator = object : Iterator { + override fun hasNext() = false + + override fun next(): R = null!! + } + + suspend tailrec fun next(): R { + return when { + currentIt.hasNext() -> currentIt.next() + underlying.hasNext() -> { + currentIt = transform(underlying.next()) + next() + } + else -> throw IllegalArgumentException("Cannot call next() on the empty iterator") + } + } + + suspend fun hasNext() = currentIt.hasNext() || underlying.hasNext() +} + +fun builder(c: suspend () -> Unit) { + c.startCoroutine(EmptyContinuation) +} + +fun box(): String { + var res = "" + builder { + val iter = CompilerKillingIterator("ok".asIterable().iterator()) { ("" + it.toUpperCase()).asIterable().iterator() } + while (iter.hasNext()) { + res += iter.next() + } + } + return res +} \ No newline at end of file diff --git a/compiler/testData/codegen/box/coroutines/featureIntersection/tailrec.kt b/compiler/testData/codegen/box/coroutines/featureIntersection/tailrec/extention.kt similarity index 100% rename from compiler/testData/codegen/box/coroutines/featureIntersection/tailrec.kt rename to compiler/testData/codegen/box/coroutines/featureIntersection/tailrec/extention.kt diff --git a/compiler/testData/codegen/box/coroutines/featureIntersection/tailrec/infixCall.kt b/compiler/testData/codegen/box/coroutines/featureIntersection/tailrec/infixCall.kt new file mode 100644 index 00000000000..f0287ad5fb0 --- /dev/null +++ b/compiler/testData/codegen/box/coroutines/featureIntersection/tailrec/infixCall.kt @@ -0,0 +1,25 @@ +// WITH_RUNTIME +// WITH_COROUTINES +// IGNORE_BACKEND_WITHOUT_CHECK: JS + +import helpers.* +import kotlin.coroutines.experimental.* + +tailrec suspend infix fun Int.test(x : Int) : Int { + if (this > 1) { + return (this - 1) test x + } + return this +} + +fun builder(c: suspend () -> Unit) { + c.startCoroutine(EmptyContinuation) +} + +fun box() : String { + var res = "" + builder { + res = if (1000000.test(1000000) == 1) "OK" else "FAIL" + } + return res +} \ No newline at end of file diff --git a/compiler/testData/codegen/box/coroutines/featureIntersection/tailrec/infixRecursiveCall.kt b/compiler/testData/codegen/box/coroutines/featureIntersection/tailrec/infixRecursiveCall.kt new file mode 100644 index 00000000000..f4088e3a83b --- /dev/null +++ b/compiler/testData/codegen/box/coroutines/featureIntersection/tailrec/infixRecursiveCall.kt @@ -0,0 +1,23 @@ +// WITH_RUNTIME +// WITH_COROUTINES +// IGNORE_BACKEND_WITHOUT_CHECK: JS + +import helpers.* +import kotlin.coroutines.experimental.* + +tailrec suspend infix fun Int.foo(x: Int) { + if (x == 0) return + val xx = x - 1 + return 1 foo xx +} + +fun builder(c: suspend () -> Unit) { + c.startCoroutine(EmptyContinuation) +} + +fun box(): String { + builder { + 1 foo 1000000 + } + return "OK" +} \ No newline at end of file diff --git a/compiler/testData/codegen/box/coroutines/featureIntersection/tailrec/realIteratorFoldl.kt b/compiler/testData/codegen/box/coroutines/featureIntersection/tailrec/realIteratorFoldl.kt new file mode 100644 index 00000000000..78b4c3cef5f --- /dev/null +++ b/compiler/testData/codegen/box/coroutines/featureIntersection/tailrec/realIteratorFoldl.kt @@ -0,0 +1,24 @@ +// WITH_RUNTIME +// WITH_COROUTINES +// IGNORE_BACKEND_WITHOUT_CHECK: JS +import helpers.* +import kotlin.coroutines.experimental.* + +tailrec suspend fun Iterator.foldl(acc : A, foldFunction : (e : T, acc : A) -> A) : A = + if (!hasNext()) acc + else foldl(foldFunction(next(), acc), foldFunction) + +fun builder(c: suspend () -> Unit) { + c.startCoroutine(EmptyContinuation) +} + +fun box() : String { + var sum = 0L + builder { + sum = (1..1000000).iterator().foldl(0) { e : Int, acc : Long -> + acc + e + } + } + + return if (sum == 500000500000) "OK" else "FAIL: $sum" +} \ No newline at end of file diff --git a/compiler/testData/codegen/box/coroutines/featureIntersection/tailrec/realStringEscape.kt b/compiler/testData/codegen/box/coroutines/featureIntersection/tailrec/realStringEscape.kt new file mode 100644 index 00000000000..0b168f575c6 --- /dev/null +++ b/compiler/testData/codegen/box/coroutines/featureIntersection/tailrec/realStringEscape.kt @@ -0,0 +1,27 @@ +// WITH_RUNTIME +// WITH_COROUTINES +import helpers.* +import kotlin.coroutines.experimental.* + +suspend fun escapeChar(c : Char) : String? = when (c) { + '\\' -> "\\\\" + '\n' -> "\\n" + '"' -> "\\\"" + else -> "" + c +} + +tailrec suspend fun String.escape(i : Int = 0, result : StringBuilder = StringBuilder()) : String = + if (i == length) result.toString() + else escape(i + 1, result.append(escapeChar(get(i)))) + +fun builder(c: suspend () -> Unit) { + c.startCoroutine(EmptyContinuation) +} + +fun box() : String { + var res = "" + builder { + res = "test me not \\".escape() + } + return if (res == "test me not \\\\") "OK" else res +} diff --git a/compiler/testData/codegen/box/coroutines/featureIntersection/tailrec/realStringRepeat.kt b/compiler/testData/codegen/box/coroutines/featureIntersection/tailrec/realStringRepeat.kt new file mode 100644 index 00000000000..2f934d73d95 --- /dev/null +++ b/compiler/testData/codegen/box/coroutines/featureIntersection/tailrec/realStringRepeat.kt @@ -0,0 +1,21 @@ +// WITH_RUNTIME +// WITH_COROUTINES +// IGNORE_BACKEND_WITHOUT_CHECK: JS +import helpers.* +import kotlin.coroutines.experimental.* + +tailrec suspend fun String.repeat(num : Int, acc : StringBuilder = StringBuilder()) : String = + if (num == 0) acc.toString() + else repeat(num - 1, acc.append(this)) + +fun builder(c: suspend () -> Unit) { + c.startCoroutine(EmptyContinuation) +} + +fun box() : String { + var s: String = "" + builder { + s = "a".repeat(10000) + } + return if (s.length == 10000) "OK" else "FAIL: ${s.length}" +} diff --git a/compiler/testData/codegen/box/coroutines/featureIntersection/tailrec/returnInParentheses.kt b/compiler/testData/codegen/box/coroutines/featureIntersection/tailrec/returnInParentheses.kt new file mode 100644 index 00000000000..27c6d86c079 --- /dev/null +++ b/compiler/testData/codegen/box/coroutines/featureIntersection/tailrec/returnInParentheses.kt @@ -0,0 +1,21 @@ +// WITH_RUNTIME +// WITH_COROUTINES +// IGNORE_BACKEND_WITHOUT_CHECK: JS +import helpers.* +import kotlin.coroutines.experimental.* + +tailrec suspend fun foo(x: Int) { + if (x == 0) return + (return foo(x - 1)) +} + +fun builder(c: suspend () -> Unit) { + c.startCoroutine(EmptyContinuation) +} + +fun box(): String { + builder { + foo(1000000) + } + return "OK" +} \ No newline at end of file diff --git a/compiler/testData/codegen/box/coroutines/featureIntersection/tailrec/sum.kt b/compiler/testData/codegen/box/coroutines/featureIntersection/tailrec/sum.kt new file mode 100644 index 00000000000..411725bd761 --- /dev/null +++ b/compiler/testData/codegen/box/coroutines/featureIntersection/tailrec/sum.kt @@ -0,0 +1,23 @@ +// WITH_RUNTIME +// WITH_COROUTINES +// IGNORE_BACKEND_WITHOUT_CHECK: JS +import helpers.* +import kotlin.coroutines.experimental.* + +tailrec suspend fun sum(x: Long, sum: Long): Long { + if (x == 0.toLong()) return sum + return sum(x - 1, sum + x) +} + +fun builder(c: suspend () -> Unit) { + c.startCoroutine(EmptyContinuation) +} + +fun box() : String { + var sum: Long = 0L + builder { + sum = sum(1000000, 0) + } + if (sum != 500000500000.toLong()) return "Fail $sum" + return "OK" +} \ No newline at end of file diff --git a/compiler/testData/codegen/box/coroutines/featureIntersection/tailrec/tailCallInBlockInParentheses.kt b/compiler/testData/codegen/box/coroutines/featureIntersection/tailrec/tailCallInBlockInParentheses.kt new file mode 100644 index 00000000000..bcb60f468f5 --- /dev/null +++ b/compiler/testData/codegen/box/coroutines/featureIntersection/tailrec/tailCallInBlockInParentheses.kt @@ -0,0 +1,23 @@ +// WITH_RUNTIME +// WITH_COROUTINES +// IGNORE_BACKEND_WITHOUT_CHECK: JS +import helpers.* +import kotlin.coroutines.experimental.* + +tailrec suspend fun foo(x: Int) { + return if (x > 0) { + (foo(x - 1)) + } + else Unit +} + +fun builder(c: suspend () -> Unit) { + c.startCoroutine(EmptyContinuation) +} + +fun box(): String { + builder { + foo(1000000) + } + return "OK" +} diff --git a/compiler/testData/codegen/box/coroutines/featureIntersection/tailrec/tailCallInParentheses.kt b/compiler/testData/codegen/box/coroutines/featureIntersection/tailrec/tailCallInParentheses.kt new file mode 100644 index 00000000000..ccbf0fb9b94 --- /dev/null +++ b/compiler/testData/codegen/box/coroutines/featureIntersection/tailrec/tailCallInParentheses.kt @@ -0,0 +1,21 @@ +// WITH_RUNTIME +// WITH_COROUTINES +// IGNORE_BACKEND_WITHOUT_CHECK: JS +import helpers.* +import kotlin.coroutines.experimental.* + +tailrec suspend fun foo(x: Int) { + if (x == 0) return + return (foo(x - 1)) +} + +fun builder(c: suspend () -> Unit) { + c.startCoroutine(EmptyContinuation) +} + +fun box(): String { + builder { + foo(1000000) + } + return "OK" +} \ No newline at end of file diff --git a/compiler/testData/codegen/box/coroutines/featureIntersection/tailrec/whenWithIs.kt b/compiler/testData/codegen/box/coroutines/featureIntersection/tailrec/whenWithIs.kt new file mode 100644 index 00000000000..d7e99c21f6a --- /dev/null +++ b/compiler/testData/codegen/box/coroutines/featureIntersection/tailrec/whenWithIs.kt @@ -0,0 +1,31 @@ +// WITH_RUNTIME +// WITH_COROUTINES +// IGNORE_BACKEND_WITHOUT_CHECK: JS +import helpers.* +import kotlin.coroutines.experimental.* + +tailrec suspend fun withWhen(counter : Int, d : Any) : Int = + if (counter == 0) { + 0 + } + else if (counter == 5) { + withWhen(counter - 1, 999) + } + else + when (d) { + is String -> withWhen(counter - 1, "is String") + is Number -> withWhen(counter, "is Number") + else -> throw IllegalStateException() + } + +fun builder(c: suspend () -> Unit) { + c.startCoroutine(EmptyContinuation) +} + +fun box() : String { + var res = -1 + builder { + res = withWhen(100000, "test") + } + return if (res == 0) "OK" else "FAIL" +} diff --git a/compiler/tests-ir-jvm/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxCodegenTestGenerated.java b/compiler/tests-ir-jvm/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxCodegenTestGenerated.java index 6855b264b92..7974060704a 100644 --- a/compiler/tests-ir-jvm/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxCodegenTestGenerated.java +++ b/compiler/tests-ir-jvm/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxCodegenTestGenerated.java @@ -6325,10 +6325,91 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes doTest(fileName); } - @TestMetadata("tailrec.kt") - public void testTailrec() throws Exception { - String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/featureIntersection/tailrec.kt"); - doTest(fileName); + @TestMetadata("compiler/testData/codegen/box/coroutines/featureIntersection/tailrec") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class Tailrec extends AbstractIrBlackBoxCodegenTest { + public void testAllFilesPresentInTailrec() throws Exception { + KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/box/coroutines/featureIntersection/tailrec"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.JVM, true); + } + + @TestMetadata("controlFlowIf.kt") + public void testControlFlowIf() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/featureIntersection/tailrec/controlFlowIf.kt"); + doTest(fileName); + } + + @TestMetadata("controlFlowWhen.kt") + public void testControlFlowWhen() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/featureIntersection/tailrec/controlFlowWhen.kt"); + doTest(fileName); + } + + @TestMetadata("extention.kt") + public void testExtention() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/featureIntersection/tailrec/extention.kt"); + doTest(fileName); + } + + @TestMetadata("infixCall.kt") + public void testInfixCall() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/featureIntersection/tailrec/infixCall.kt"); + doTest(fileName); + } + + @TestMetadata("infixRecursiveCall.kt") + public void testInfixRecursiveCall() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/featureIntersection/tailrec/infixRecursiveCall.kt"); + doTest(fileName); + } + + @TestMetadata("realIteratorFoldl.kt") + public void testRealIteratorFoldl() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/featureIntersection/tailrec/realIteratorFoldl.kt"); + doTest(fileName); + } + + @TestMetadata("realStringEscape.kt") + public void testRealStringEscape() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/featureIntersection/tailrec/realStringEscape.kt"); + doTest(fileName); + } + + @TestMetadata("realStringRepeat.kt") + public void testRealStringRepeat() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/featureIntersection/tailrec/realStringRepeat.kt"); + doTest(fileName); + } + + @TestMetadata("returnInParentheses.kt") + public void testReturnInParentheses() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/featureIntersection/tailrec/returnInParentheses.kt"); + doTest(fileName); + } + + @TestMetadata("sum.kt") + public void testSum() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/featureIntersection/tailrec/sum.kt"); + doTest(fileName); + } + + @TestMetadata("tailCallInBlockInParentheses.kt") + public void testTailCallInBlockInParentheses() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/featureIntersection/tailrec/tailCallInBlockInParentheses.kt"); + doTest(fileName); + } + + @TestMetadata("tailCallInParentheses.kt") + public void testTailCallInParentheses() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/featureIntersection/tailrec/tailCallInParentheses.kt"); + doTest(fileName); + } + + @TestMetadata("whenWithIs.kt") + public void testWhenWithIs() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/featureIntersection/tailrec/whenWithIs.kt"); + doTest(fileName); + } } } diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java index 1656e2d424e..4c8649a1172 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java @@ -6325,10 +6325,91 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest { doTest(fileName); } - @TestMetadata("tailrec.kt") - public void testTailrec() throws Exception { - String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/featureIntersection/tailrec.kt"); - doTest(fileName); + @TestMetadata("compiler/testData/codegen/box/coroutines/featureIntersection/tailrec") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class Tailrec extends AbstractBlackBoxCodegenTest { + public void testAllFilesPresentInTailrec() throws Exception { + KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/box/coroutines/featureIntersection/tailrec"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.JVM, true); + } + + @TestMetadata("controlFlowIf.kt") + public void testControlFlowIf() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/featureIntersection/tailrec/controlFlowIf.kt"); + doTest(fileName); + } + + @TestMetadata("controlFlowWhen.kt") + public void testControlFlowWhen() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/featureIntersection/tailrec/controlFlowWhen.kt"); + doTest(fileName); + } + + @TestMetadata("extention.kt") + public void testExtention() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/featureIntersection/tailrec/extention.kt"); + doTest(fileName); + } + + @TestMetadata("infixCall.kt") + public void testInfixCall() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/featureIntersection/tailrec/infixCall.kt"); + doTest(fileName); + } + + @TestMetadata("infixRecursiveCall.kt") + public void testInfixRecursiveCall() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/featureIntersection/tailrec/infixRecursiveCall.kt"); + doTest(fileName); + } + + @TestMetadata("realIteratorFoldl.kt") + public void testRealIteratorFoldl() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/featureIntersection/tailrec/realIteratorFoldl.kt"); + doTest(fileName); + } + + @TestMetadata("realStringEscape.kt") + public void testRealStringEscape() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/featureIntersection/tailrec/realStringEscape.kt"); + doTest(fileName); + } + + @TestMetadata("realStringRepeat.kt") + public void testRealStringRepeat() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/featureIntersection/tailrec/realStringRepeat.kt"); + doTest(fileName); + } + + @TestMetadata("returnInParentheses.kt") + public void testReturnInParentheses() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/featureIntersection/tailrec/returnInParentheses.kt"); + doTest(fileName); + } + + @TestMetadata("sum.kt") + public void testSum() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/featureIntersection/tailrec/sum.kt"); + doTest(fileName); + } + + @TestMetadata("tailCallInBlockInParentheses.kt") + public void testTailCallInBlockInParentheses() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/featureIntersection/tailrec/tailCallInBlockInParentheses.kt"); + doTest(fileName); + } + + @TestMetadata("tailCallInParentheses.kt") + public void testTailCallInParentheses() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/featureIntersection/tailrec/tailCallInParentheses.kt"); + doTest(fileName); + } + + @TestMetadata("whenWithIs.kt") + public void testWhenWithIs() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/featureIntersection/tailrec/whenWithIs.kt"); + doTest(fileName); + } } } diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java index 4244bf6adb4..aedfbdac1b7 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java @@ -6325,10 +6325,91 @@ public class LightAnalysisModeTestGenerated extends AbstractLightAnalysisModeTes doTest(fileName); } - @TestMetadata("tailrec.kt") - public void testTailrec() throws Exception { - String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/featureIntersection/tailrec.kt"); - doTest(fileName); + @TestMetadata("compiler/testData/codegen/box/coroutines/featureIntersection/tailrec") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class Tailrec extends AbstractLightAnalysisModeTest { + public void testAllFilesPresentInTailrec() throws Exception { + KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/box/coroutines/featureIntersection/tailrec"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.JVM, true); + } + + @TestMetadata("controlFlowIf.kt") + public void testControlFlowIf() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/featureIntersection/tailrec/controlFlowIf.kt"); + doTest(fileName); + } + + @TestMetadata("controlFlowWhen.kt") + public void testControlFlowWhen() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/featureIntersection/tailrec/controlFlowWhen.kt"); + doTest(fileName); + } + + @TestMetadata("extention.kt") + public void testExtention() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/featureIntersection/tailrec/extention.kt"); + doTest(fileName); + } + + @TestMetadata("infixCall.kt") + public void testInfixCall() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/featureIntersection/tailrec/infixCall.kt"); + doTest(fileName); + } + + @TestMetadata("infixRecursiveCall.kt") + public void testInfixRecursiveCall() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/featureIntersection/tailrec/infixRecursiveCall.kt"); + doTest(fileName); + } + + @TestMetadata("realIteratorFoldl.kt") + public void testRealIteratorFoldl() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/featureIntersection/tailrec/realIteratorFoldl.kt"); + doTest(fileName); + } + + @TestMetadata("realStringEscape.kt") + public void testRealStringEscape() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/featureIntersection/tailrec/realStringEscape.kt"); + doTest(fileName); + } + + @TestMetadata("realStringRepeat.kt") + public void testRealStringRepeat() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/featureIntersection/tailrec/realStringRepeat.kt"); + doTest(fileName); + } + + @TestMetadata("returnInParentheses.kt") + public void testReturnInParentheses() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/featureIntersection/tailrec/returnInParentheses.kt"); + doTest(fileName); + } + + @TestMetadata("sum.kt") + public void testSum() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/featureIntersection/tailrec/sum.kt"); + doTest(fileName); + } + + @TestMetadata("tailCallInBlockInParentheses.kt") + public void testTailCallInBlockInParentheses() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/featureIntersection/tailrec/tailCallInBlockInParentheses.kt"); + doTest(fileName); + } + + @TestMetadata("tailCallInParentheses.kt") + public void testTailCallInParentheses() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/featureIntersection/tailrec/tailCallInParentheses.kt"); + doTest(fileName); + } + + @TestMetadata("whenWithIs.kt") + public void testWhenWithIs() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/featureIntersection/tailrec/whenWithIs.kt"); + doTest(fileName); + } } } diff --git a/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/JsCodegenBoxTestGenerated.java b/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/JsCodegenBoxTestGenerated.java index 47bbe38e1d1..6f3ae6da31f 100644 --- a/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/JsCodegenBoxTestGenerated.java +++ b/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/JsCodegenBoxTestGenerated.java @@ -7644,10 +7644,91 @@ public class JsCodegenBoxTestGenerated extends AbstractJsCodegenBoxTest { doTest(fileName); } - @TestMetadata("tailrec.kt") - public void testTailrec() throws Exception { - String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/featureIntersection/tailrec.kt"); - doTest(fileName); + @TestMetadata("compiler/testData/codegen/box/coroutines/featureIntersection/tailrec") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class Tailrec extends AbstractJsCodegenBoxTest { + @TestMetadata("infixCall.kt") + public void ignoreInfixCall() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/featureIntersection/tailrec/infixCall.kt"); + doTest(fileName); + } + + @TestMetadata("infixRecursiveCall.kt") + public void ignoreInfixRecursiveCall() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/featureIntersection/tailrec/infixRecursiveCall.kt"); + doTest(fileName); + } + + @TestMetadata("realIteratorFoldl.kt") + public void ignoreRealIteratorFoldl() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/featureIntersection/tailrec/realIteratorFoldl.kt"); + doTest(fileName); + } + + @TestMetadata("realStringRepeat.kt") + public void ignoreRealStringRepeat() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/featureIntersection/tailrec/realStringRepeat.kt"); + doTest(fileName); + } + + @TestMetadata("returnInParentheses.kt") + public void ignoreReturnInParentheses() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/featureIntersection/tailrec/returnInParentheses.kt"); + doTest(fileName); + } + + @TestMetadata("sum.kt") + public void ignoreSum() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/featureIntersection/tailrec/sum.kt"); + doTest(fileName); + } + + @TestMetadata("tailCallInBlockInParentheses.kt") + public void ignoreTailCallInBlockInParentheses() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/featureIntersection/tailrec/tailCallInBlockInParentheses.kt"); + doTest(fileName); + } + + @TestMetadata("tailCallInParentheses.kt") + public void ignoreTailCallInParentheses() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/featureIntersection/tailrec/tailCallInParentheses.kt"); + doTest(fileName); + } + + @TestMetadata("whenWithIs.kt") + public void ignoreWhenWithIs() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/featureIntersection/tailrec/whenWithIs.kt"); + doTest(fileName); + } + + public void testAllFilesPresentInTailrec() throws Exception { + KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/box/coroutines/featureIntersection/tailrec"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.JS, true); + } + + @TestMetadata("controlFlowIf.kt") + public void testControlFlowIf() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/featureIntersection/tailrec/controlFlowIf.kt"); + doTest(fileName); + } + + @TestMetadata("controlFlowWhen.kt") + public void testControlFlowWhen() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/featureIntersection/tailrec/controlFlowWhen.kt"); + doTest(fileName); + } + + @TestMetadata("extention.kt") + public void testExtention() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/featureIntersection/tailrec/extention.kt"); + doTest(fileName); + } + + @TestMetadata("realStringEscape.kt") + public void testRealStringEscape() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/featureIntersection/tailrec/realStringEscape.kt"); + doTest(fileName); + } } }