From a4ad995f31f9e6a9bdda19c1e87443ee8a51cbb4 Mon Sep 17 00:00:00 2001 From: Mikhail Glukhikh Date: Mon, 16 May 2016 18:51:16 +0300 Subject: [PATCH] Overridden functions using default arguments in recursive call are no more considered tail recursive #KT-4285 Fixed --- .../cfg/ControlFlowInformationProvider.java | 3 ++ .../tailRecursion/defaultArgsOverridden.kt | 11 +++++++ .../tailRecursion/defaultArgsOverridden.txt | 19 +++++++++++ .../diagnostics/tests/tailRecOverridden.kt | 32 +++++++++++++++++++ .../diagnostics/tests/tailRecOverridden.txt | 30 +++++++++++++++++ .../checkers/DiagnosticsTestGenerated.java | 12 +++++++ .../codegen/BlackBoxCodegenTestGenerated.java | 6 ++++ 7 files changed, 113 insertions(+) create mode 100644 compiler/testData/codegen/box/diagnostics/functions/tailRecursion/defaultArgsOverridden.kt create mode 100644 compiler/testData/codegen/box/diagnostics/functions/tailRecursion/defaultArgsOverridden.txt create mode 100644 compiler/testData/diagnostics/tests/tailRecOverridden.kt create mode 100644 compiler/testData/diagnostics/tests/tailRecOverridden.txt diff --git a/compiler/frontend/src/org/jetbrains/kotlin/cfg/ControlFlowInformationProvider.java b/compiler/frontend/src/org/jetbrains/kotlin/cfg/ControlFlowInformationProvider.java index 1c4ce58f4fd..32cdb678d2f 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/cfg/ControlFlowInformationProvider.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/cfg/ControlFlowInformationProvider.java @@ -957,6 +957,9 @@ public class ControlFlowInformationProvider { // is this a recursive call? CallableDescriptor functionDescriptor = resolvedCall.getResultingDescriptor(); if (!functionDescriptor.getOriginal().equals(subroutineDescriptor)) return; + // Overridden functions using default arguments at tail call are not included: KT-4285 + if (resolvedCall.getCall().getValueArguments().size() != functionDescriptor.getValueParameters().size() && + !functionDescriptor.getOverriddenDescriptors().isEmpty()) return; KtElement element = callInstruction.getElement(); //noinspection unchecked diff --git a/compiler/testData/codegen/box/diagnostics/functions/tailRecursion/defaultArgsOverridden.kt b/compiler/testData/codegen/box/diagnostics/functions/tailRecursion/defaultArgsOverridden.kt new file mode 100644 index 00000000000..48d5a612d52 --- /dev/null +++ b/compiler/testData/codegen/box/diagnostics/functions/tailRecursion/defaultArgsOverridden.kt @@ -0,0 +1,11 @@ +open class A { + open fun foo(s: String = "OK") = s +} + +class B : A() { + override tailrec fun foo(s: String): String { + return if (s == "OK") s else foo() + } +} + +fun box() = B().foo("FAIL") \ No newline at end of file diff --git a/compiler/testData/codegen/box/diagnostics/functions/tailRecursion/defaultArgsOverridden.txt b/compiler/testData/codegen/box/diagnostics/functions/tailRecursion/defaultArgsOverridden.txt new file mode 100644 index 00000000000..f5a8d3fb277 --- /dev/null +++ b/compiler/testData/codegen/box/diagnostics/functions/tailRecursion/defaultArgsOverridden.txt @@ -0,0 +1,19 @@ +package + +public fun box(): kotlin.String + +public open class A { + public constructor A() + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open fun foo(/*0*/ s: kotlin.String = ...): kotlin.String + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String +} + +public final class B : A { + public constructor B() + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open tailrec override /*1*/ fun foo(/*0*/ s: kotlin.String = ...): kotlin.String + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String +} diff --git a/compiler/testData/diagnostics/tests/tailRecOverridden.kt b/compiler/testData/diagnostics/tests/tailRecOverridden.kt new file mode 100644 index 00000000000..4656d7284df --- /dev/null +++ b/compiler/testData/diagnostics/tests/tailRecOverridden.kt @@ -0,0 +1,32 @@ +// See also KT-4285 +open class A { + open fun foo(x: Int = 0) {} + + open fun gav(y: Int = 1, z: Int = 2) {} +} + +class B: A() { + tailrec override fun foo(x: Int) { + foo() + } + + tailrec override fun gav(y: Int, z: Int) { + gav(y) + } + + tailrec fun bar(y: Double): Double = bar(y * 2.0) +} + +class C: A() { + tailrec override fun foo(x: Int) { + foo(0) + } + + tailrec override fun gav(y: Int, z: Int) { + gav(y - 1, z - 1) + } + + tailrec fun bar(y: Int = 1, z: Int = 2) { + bar(z) + } +} \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/tailRecOverridden.txt b/compiler/testData/diagnostics/tests/tailRecOverridden.txt new file mode 100644 index 00000000000..f1e0f302252 --- /dev/null +++ b/compiler/testData/diagnostics/tests/tailRecOverridden.txt @@ -0,0 +1,30 @@ +package + +public open class A { + public constructor A() + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open fun foo(/*0*/ x: kotlin.Int = ...): kotlin.Unit + public open fun gav(/*0*/ y: kotlin.Int = ..., /*1*/ z: kotlin.Int = ...): kotlin.Unit + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String +} + +public final class B : A { + public constructor B() + public final tailrec fun bar(/*0*/ y: kotlin.Double): kotlin.Double + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open tailrec override /*1*/ fun foo(/*0*/ x: kotlin.Int = ...): kotlin.Unit + public open tailrec override /*1*/ fun gav(/*0*/ y: kotlin.Int = ..., /*1*/ z: kotlin.Int = ...): kotlin.Unit + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String +} + +public final class C : A { + public constructor C() + public final tailrec fun bar(/*0*/ y: kotlin.Int = ..., /*1*/ z: kotlin.Int = ...): kotlin.Unit + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open tailrec override /*1*/ fun foo(/*0*/ x: kotlin.Int = ...): kotlin.Unit + public open tailrec override /*1*/ fun gav(/*0*/ y: kotlin.Int = ..., /*1*/ z: kotlin.Int = ...): kotlin.Unit + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String +} diff --git a/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java index 9bbd963111a..715cbe64527 100644 --- a/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java @@ -679,6 +679,12 @@ public class DiagnosticsTestGenerated extends AbstractDiagnosticsTest { doTest(fileName); } + @TestMetadata("tailRecOverridden.kt") + public void testTailRecOverridden() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/tailRecOverridden.kt"); + doTest(fileName); + } + @TestMetadata("TraitOverrideObjectMethods.kt") public void testTraitOverrideObjectMethods() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/TraitOverrideObjectMethods.kt"); @@ -19772,6 +19778,12 @@ public class DiagnosticsTestGenerated extends AbstractDiagnosticsTest { doTest(fileName); } + @TestMetadata("defaultArgsOverridden.kt") + public void testDefaultArgsOverridden() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/diagnostics/functions/tailRecursion/defaultArgsOverridden.kt"); + doTest(fileName); + } + @TestMetadata("extensionTailCall.kt") public void testExtensionTailCall() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/diagnostics/functions/tailRecursion/extensionTailCall.kt"); diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java index 8996ae6dc0e..97b3b9744db 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java @@ -5135,6 +5135,12 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest { doTest(fileName); } + @TestMetadata("defaultArgsOverridden.kt") + public void testDefaultArgsOverridden() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/diagnostics/functions/tailRecursion/defaultArgsOverridden.kt"); + doTest(fileName); + } + @TestMetadata("extensionTailCall.kt") public void testExtensionTailCall() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/diagnostics/functions/tailRecursion/extensionTailCall.kt");