From adae629258abc0219df10dddb750ddb672c5d533 Mon Sep 17 00:00:00 2001 From: Mikhael Bogdanov Date: Tue, 15 Oct 2019 09:07:54 +0200 Subject: [PATCH] Report diagnostic on virtual tailrec function #KT-18541 Fixed --- .../fir/FirDiagnosticsSmokeTestGenerated.java | 10 + .../jetbrains/kotlin/diagnostics/Errors.java | 2 + .../rendering/DefaultErrorMessages.java | 3 + .../resolve/PlatformConfiguratorBase.kt | 3 +- .../checkers/TailrecFunctionChecker.kt | 28 +++ .../resolve/inline/InlineAnalyzerExtension.kt | 10 +- .../kotlin/resolve/ktDescriptorUtil.kt | 11 ++ .../tests/tailRecOnVirtualMember.kt | 171 ++++++++++++++++++ .../tests/tailRecOnVirtualMember.txt | 125 +++++++++++++ .../tests/tailRecOnVirtualMemberError.kt | 170 +++++++++++++++++ .../tests/tailRecOnVirtualMemberError.txt | 125 +++++++++++++ .../checkers/DiagnosticsTestGenerated.java | 10 + .../DiagnosticsUsingJavacTestGenerated.java | 10 + .../kotlin/config/LanguageVersionSettings.kt | 1 + 14 files changed, 671 insertions(+), 8 deletions(-) create mode 100644 compiler/frontend/src/org/jetbrains/kotlin/resolve/checkers/TailrecFunctionChecker.kt create mode 100644 compiler/testData/diagnostics/tests/tailRecOnVirtualMember.kt create mode 100644 compiler/testData/diagnostics/tests/tailRecOnVirtualMember.txt create mode 100644 compiler/testData/diagnostics/tests/tailRecOnVirtualMemberError.kt create mode 100644 compiler/testData/diagnostics/tests/tailRecOnVirtualMemberError.txt diff --git a/compiler/fir/resolve/tests/org/jetbrains/kotlin/fir/FirDiagnosticsSmokeTestGenerated.java b/compiler/fir/resolve/tests/org/jetbrains/kotlin/fir/FirDiagnosticsSmokeTestGenerated.java index 6d471a80d82..3cd82f0d78e 100644 --- a/compiler/fir/resolve/tests/org/jetbrains/kotlin/fir/FirDiagnosticsSmokeTestGenerated.java +++ b/compiler/fir/resolve/tests/org/jetbrains/kotlin/fir/FirDiagnosticsSmokeTestGenerated.java @@ -609,6 +609,16 @@ public class FirDiagnosticsSmokeTestGenerated extends AbstractFirDiagnosticsSmok runTest("compiler/testData/diagnostics/tests/SyntaxErrorInTestHighlightingEof.kt"); } + @TestMetadata("tailRecOnVirtualMember.kt") + public void testTailRecOnVirtualMember() throws Exception { + runTest("compiler/testData/diagnostics/tests/tailRecOnVirtualMember.kt"); + } + + @TestMetadata("tailRecOnVirtualMemberError.kt") + public void testTailRecOnVirtualMemberError() throws Exception { + runTest("compiler/testData/diagnostics/tests/tailRecOnVirtualMemberError.kt"); + } + @TestMetadata("tailRecOverridden.kt") public void testTailRecOverridden() throws Exception { runTest("compiler/testData/diagnostics/tests/tailRecOverridden.kt"); diff --git a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/Errors.java b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/Errors.java index 20c78ecc7ea..a2178ab0fed 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/Errors.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/Errors.java @@ -585,6 +585,8 @@ public interface Errors { DiagnosticFactory0 VALUE_PARAMETER_WITH_NO_TYPE_ANNOTATION = DiagnosticFactory0.create(ERROR); DiagnosticFactory0 NO_TAIL_CALLS_FOUND = DiagnosticFactory0.create(WARNING, DECLARATION_SIGNATURE); + DiagnosticFactory0 TAILREC_ON_VIRTUAL_MEMBER = DiagnosticFactory0.create(WARNING, DECLARATION_SIGNATURE); + DiagnosticFactory0 TAILREC_ON_VIRTUAL_MEMBER_ERROR = DiagnosticFactory0.create(ERROR, DECLARATION_SIGNATURE); DiagnosticFactory0 ANONYMOUS_FUNCTION_PARAMETER_WITH_DEFAULT_VALUE = DiagnosticFactory0.create(ERROR, PARAMETER_DEFAULT_VALUE); diff --git a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/rendering/DefaultErrorMessages.java b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/rendering/DefaultErrorMessages.java index 1afcd339f6c..5f11d00f6e9 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/rendering/DefaultErrorMessages.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/rendering/DefaultErrorMessages.java @@ -620,6 +620,9 @@ public class DefaultErrorMessages { MAP.put(ILLEGAL_SELECTOR, "The expression cannot be a selector (occur after a dot)"); MAP.put(NO_TAIL_CALLS_FOUND, "A function is marked as tail-recursive but no tail calls are found"); + MAP.put(TAILREC_ON_VIRTUAL_MEMBER, "Tailrec on open members is deprecated"); + MAP.put(TAILREC_ON_VIRTUAL_MEMBER_ERROR, "Tailrec is not allowed on open members"); + MAP.put(VALUE_PARAMETER_WITH_NO_TYPE_ANNOTATION, "A type annotation is required on a value parameter"); MAP.put(BREAK_OR_CONTINUE_OUTSIDE_A_LOOP, "'break' and 'continue' are only allowed inside a loop"); MAP.put(BREAK_OR_CONTINUE_IN_WHEN, "'break' and 'continue' are not allowed in 'when' statements. Consider using labels to continue/break from the outer loop"); diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/PlatformConfiguratorBase.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/PlatformConfiguratorBase.kt index 84c77cd6fc5..3c90fb0ece8 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/PlatformConfiguratorBase.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/PlatformConfiguratorBase.kt @@ -31,7 +31,8 @@ private val DEFAULT_DECLARATION_CHECKERS = listOf( AnnotationClassTargetAndRetentionChecker(), ReservedMembersAndConstructsForInlineClass(), ResultClassInReturnTypeChecker(), - LocalVariableTypeParametersChecker() + LocalVariableTypeParametersChecker(), + TailrecFunctionChecker ) private val DEFAULT_CALL_CHECKERS = listOf( diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/checkers/TailrecFunctionChecker.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/checkers/TailrecFunctionChecker.kt new file mode 100644 index 00000000000..e9e68347674 --- /dev/null +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/checkers/TailrecFunctionChecker.kt @@ -0,0 +1,28 @@ +/* + * Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors. + * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. + */ + +package org.jetbrains.kotlin.resolve.checkers + +import org.jetbrains.kotlin.config.LanguageFeature +import org.jetbrains.kotlin.descriptors.DeclarationDescriptor +import org.jetbrains.kotlin.descriptors.FunctionDescriptor +import org.jetbrains.kotlin.diagnostics.Errors +import org.jetbrains.kotlin.psi.KtDeclaration +import org.jetbrains.kotlin.psi.KtNamedFunction +import org.jetbrains.kotlin.resolve.isEffectivelyFinal + +object TailrecFunctionChecker : DeclarationChecker { + override fun check(declaration: KtDeclaration, descriptor: DeclarationDescriptor, context: DeclarationCheckerContext) { + if (declaration !is KtNamedFunction || descriptor !is FunctionDescriptor || !descriptor.isTailrec) return + + if (descriptor.isEffectivelyFinal(false)) return + + if (!context.languageVersionSettings.supportsFeature(LanguageFeature.ProhibitTailrecOnVirtualMember)) { + context.trace.report(Errors.TAILREC_ON_VIRTUAL_MEMBER.on(declaration)) + } else { + context.trace.report(Errors.TAILREC_ON_VIRTUAL_MEMBER_ERROR.on(declaration)) + } + } +} \ No newline at end of file diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/inline/InlineAnalyzerExtension.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/inline/InlineAnalyzerExtension.kt index bef3f92f7c2..52eccf96916 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/inline/InlineAnalyzerExtension.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/inline/InlineAnalyzerExtension.kt @@ -28,6 +28,7 @@ import org.jetbrains.kotlin.resolve.BindingContext import org.jetbrains.kotlin.resolve.BindingTrace import org.jetbrains.kotlin.resolve.calls.components.hasDefaultValue import org.jetbrains.kotlin.resolve.descriptorUtil.declaresOrInheritsDefaultValue +import org.jetbrains.kotlin.resolve.isEffectivelyFinal class InlineAnalyzerExtension( private val reasonableInlineRules: Iterable, @@ -159,7 +160,8 @@ class InlineAnalyzerExtension( } } - if (callableDescriptor.isEffectivelyFinal()) { + //TODO: actually it should be isEffectivelyFinal(false), but looks like it requires committee decision: KT-34372) + if (callableDescriptor.isEffectivelyFinal(true)) { if (overridesAnything) { trace.report(Errors.OVERRIDE_BY_INLINE.on(functionOrProperty)) } @@ -168,12 +170,6 @@ class InlineAnalyzerExtension( trace.report(Errors.DECLARATION_CANT_BE_INLINED.on(functionOrProperty)) } - private fun CallableMemberDescriptor.isEffectivelyFinal(): Boolean = - modality == Modality.FINAL || - containingDeclaration.let { containingDeclaration -> - containingDeclaration is ClassDescriptor && containingDeclaration.modality == Modality.FINAL - } - private fun checkHasInlinableAndNullability(functionDescriptor: FunctionDescriptor, function: KtFunction, trace: BindingTrace) { var hasInlineArgs = false function.valueParameters.zip(functionDescriptor.valueParameters).forEach { (parameter, descriptor) -> diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/ktDescriptorUtil.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/ktDescriptorUtil.kt index 0af3094a4a8..783059ce010 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/ktDescriptorUtil.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/ktDescriptorUtil.kt @@ -5,7 +5,10 @@ package org.jetbrains.kotlin.resolve +import org.jetbrains.kotlin.descriptors.CallableMemberDescriptor +import org.jetbrains.kotlin.descriptors.ClassDescriptor import org.jetbrains.kotlin.descriptors.FunctionDescriptor +import org.jetbrains.kotlin.descriptors.Modality import org.jetbrains.kotlin.types.DeferredType import org.jetbrains.kotlin.types.KotlinType import org.jetbrains.kotlin.types.typeUtil.contains @@ -24,3 +27,11 @@ fun FunctionDescriptor.isFunctionForExpectTypeFromCastFeature(): Boolean { return true } + + +internal fun CallableMemberDescriptor.isEffectivelyFinal(ignoreEnumClassFinality: Boolean): Boolean = + modality == Modality.FINAL || + containingDeclaration.let { parent -> + (ignoreEnumClassFinality || !DescriptorUtils.isEnumClass(parent)) && + parent is ClassDescriptor && parent.modality == Modality.FINAL + } \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/tailRecOnVirtualMember.kt b/compiler/testData/diagnostics/tests/tailRecOnVirtualMember.kt new file mode 100644 index 00000000000..b04804e2aad --- /dev/null +++ b/compiler/testData/diagnostics/tests/tailRecOnVirtualMember.kt @@ -0,0 +1,171 @@ +//!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) + } +} \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/tailRecOnVirtualMember.txt b/compiler/testData/diagnostics/tests/tailRecOnVirtualMember.txt new file mode 100644 index 00000000000..9206c55d728 --- /dev/null +++ b/compiler/testData/diagnostics/tests/tailRecOnVirtualMember.txt @@ -0,0 +1,125 @@ +package + +public val z: A + +public open class A { + public constructor A() + internal final tailrec fun baa(/*0*/ y: kotlin.Int): kotlin.Unit + internal open tailrec fun bar(/*0*/ y: kotlin.Int): kotlin.Unit + protected open tailrec fun baz(/*0*/ y: kotlin.Int): kotlin.Unit + private final tailrec fun boo(/*0*/ y: kotlin.Int): kotlin.Unit + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open tailrec fun foo(/*0*/ x: 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 open class B : A { + public constructor B() + internal final override /*1*/ tailrec /*fake_override*/ fun baa(/*0*/ y: kotlin.Int): kotlin.Unit + internal final override /*1*/ tailrec fun bar(/*0*/ y: kotlin.Int): kotlin.Unit + protected final override /*1*/ tailrec fun baz(/*0*/ y: kotlin.Int): kotlin.Unit + invisible_fake final override /*1*/ tailrec /*fake_override*/ fun boo(/*0*/ y: kotlin.Int): kotlin.Unit + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public final override /*1*/ tailrec fun foo(/*0*/ x: 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 open class C : A { + public constructor C() + internal final override /*1*/ tailrec /*fake_override*/ fun baa(/*0*/ y: kotlin.Int): kotlin.Unit + internal open override /*1*/ tailrec fun bar(/*0*/ y: kotlin.Int): kotlin.Unit + protected open override /*1*/ tailrec fun baz(/*0*/ y: kotlin.Int): kotlin.Unit + invisible_fake final override /*1*/ tailrec /*fake_override*/ fun boo(/*0*/ y: kotlin.Int): kotlin.Unit + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*1*/ tailrec fun foo(/*0*/ x: 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 object D : A { + private constructor D() + internal final override /*1*/ tailrec /*fake_override*/ fun baa(/*0*/ y: kotlin.Int): kotlin.Unit + internal open override /*1*/ tailrec fun bar(/*0*/ y: kotlin.Int): kotlin.Unit + protected open override /*1*/ tailrec fun baz(/*0*/ y: kotlin.Int): kotlin.Unit + invisible_fake final override /*1*/ tailrec /*fake_override*/ fun boo(/*0*/ y: kotlin.Int): kotlin.Unit + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*1*/ tailrec fun foo(/*0*/ x: 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 sealed class E : A { + private constructor E() + internal final override /*1*/ tailrec /*fake_override*/ fun baa(/*0*/ y: kotlin.Int): kotlin.Unit + internal open override /*1*/ tailrec fun bar(/*0*/ y: kotlin.Int): kotlin.Unit + protected open override /*1*/ tailrec fun baz(/*0*/ y: kotlin.Int): kotlin.Unit + invisible_fake final override /*1*/ tailrec /*fake_override*/ fun boo(/*0*/ y: kotlin.Int): kotlin.Unit + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*1*/ tailrec fun foo(/*0*/ x: 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 E1 : E { + public constructor E1() + internal final override /*1*/ tailrec /*fake_override*/ fun baa(/*0*/ y: kotlin.Int): kotlin.Unit + internal open override /*1*/ tailrec fun bar(/*0*/ y: kotlin.Int): kotlin.Unit + protected open override /*1*/ tailrec fun baz(/*0*/ y: kotlin.Int): kotlin.Unit + invisible_fake final override /*1*/ tailrec /*fake_override*/ fun boo(/*0*/ y: kotlin.Int): kotlin.Unit + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*1*/ tailrec fun foo(/*0*/ x: 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 enum class F : kotlin.Enum { + enum entry F0 + + enum entry F1 + + private constructor F() + public final override /*1*/ /*fake_override*/ val name: kotlin.String + public final override /*1*/ /*fake_override*/ val ordinal: kotlin.Int + internal final tailrec fun baa(/*0*/ y: kotlin.Int): kotlin.Unit + internal open tailrec fun bar(/*0*/ y: kotlin.Int): kotlin.Unit + protected open tailrec fun baz(/*0*/ y: kotlin.Int): kotlin.Unit + private final tailrec fun boo(/*0*/ y: kotlin.Int): kotlin.Unit + protected final override /*1*/ /*fake_override*/ fun clone(): kotlin.Any + public final override /*1*/ /*fake_override*/ fun compareTo(/*0*/ other: F): 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 open tailrec fun foo(/*0*/ x: kotlin.Int): kotlin.Unit + public final override /*1*/ /*fake_override*/ /*isHiddenForResolutionEverywhereBesideSupercalls*/ fun getDeclaringClass(): java.lang.Class! + public final override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String + + // Static members + public final /*synthesized*/ fun valueOf(/*0*/ value: kotlin.String): F + public final /*synthesized*/ fun values(): kotlin.Array +} + +public final enum class G : kotlin.Enum { + enum entry G1 + + private constructor G() + public final override /*1*/ /*fake_override*/ val name: kotlin.String + public final override /*1*/ /*fake_override*/ val ordinal: kotlin.Int + internal final tailrec fun baa(/*0*/ y: kotlin.Int): kotlin.Unit + internal open tailrec fun bar(/*0*/ y: kotlin.Int): kotlin.Unit + protected open tailrec fun baz(/*0*/ y: kotlin.Int): kotlin.Unit + private final tailrec fun boo(/*0*/ y: kotlin.Int): kotlin.Unit + protected final override /*1*/ /*fake_override*/ fun clone(): kotlin.Any + public final override /*1*/ /*fake_override*/ fun compareTo(/*0*/ other: G): 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 open tailrec fun foo(/*0*/ x: kotlin.Int): kotlin.Unit + public final override /*1*/ /*fake_override*/ /*isHiddenForResolutionEverywhereBesideSupercalls*/ fun getDeclaringClass(): java.lang.Class! + public final override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String + + // Static members + public final /*synthesized*/ fun valueOf(/*0*/ value: kotlin.String): G + public final /*synthesized*/ fun values(): kotlin.Array +} diff --git a/compiler/testData/diagnostics/tests/tailRecOnVirtualMemberError.kt b/compiler/testData/diagnostics/tests/tailRecOnVirtualMemberError.kt new file mode 100644 index 00000000000..1ccdbd6ea1a --- /dev/null +++ b/compiler/testData/diagnostics/tests/tailRecOnVirtualMemberError.kt @@ -0,0 +1,170 @@ +//!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) + } +} \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/tailRecOnVirtualMemberError.txt b/compiler/testData/diagnostics/tests/tailRecOnVirtualMemberError.txt new file mode 100644 index 00000000000..9206c55d728 --- /dev/null +++ b/compiler/testData/diagnostics/tests/tailRecOnVirtualMemberError.txt @@ -0,0 +1,125 @@ +package + +public val z: A + +public open class A { + public constructor A() + internal final tailrec fun baa(/*0*/ y: kotlin.Int): kotlin.Unit + internal open tailrec fun bar(/*0*/ y: kotlin.Int): kotlin.Unit + protected open tailrec fun baz(/*0*/ y: kotlin.Int): kotlin.Unit + private final tailrec fun boo(/*0*/ y: kotlin.Int): kotlin.Unit + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open tailrec fun foo(/*0*/ x: 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 open class B : A { + public constructor B() + internal final override /*1*/ tailrec /*fake_override*/ fun baa(/*0*/ y: kotlin.Int): kotlin.Unit + internal final override /*1*/ tailrec fun bar(/*0*/ y: kotlin.Int): kotlin.Unit + protected final override /*1*/ tailrec fun baz(/*0*/ y: kotlin.Int): kotlin.Unit + invisible_fake final override /*1*/ tailrec /*fake_override*/ fun boo(/*0*/ y: kotlin.Int): kotlin.Unit + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public final override /*1*/ tailrec fun foo(/*0*/ x: 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 open class C : A { + public constructor C() + internal final override /*1*/ tailrec /*fake_override*/ fun baa(/*0*/ y: kotlin.Int): kotlin.Unit + internal open override /*1*/ tailrec fun bar(/*0*/ y: kotlin.Int): kotlin.Unit + protected open override /*1*/ tailrec fun baz(/*0*/ y: kotlin.Int): kotlin.Unit + invisible_fake final override /*1*/ tailrec /*fake_override*/ fun boo(/*0*/ y: kotlin.Int): kotlin.Unit + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*1*/ tailrec fun foo(/*0*/ x: 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 object D : A { + private constructor D() + internal final override /*1*/ tailrec /*fake_override*/ fun baa(/*0*/ y: kotlin.Int): kotlin.Unit + internal open override /*1*/ tailrec fun bar(/*0*/ y: kotlin.Int): kotlin.Unit + protected open override /*1*/ tailrec fun baz(/*0*/ y: kotlin.Int): kotlin.Unit + invisible_fake final override /*1*/ tailrec /*fake_override*/ fun boo(/*0*/ y: kotlin.Int): kotlin.Unit + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*1*/ tailrec fun foo(/*0*/ x: 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 sealed class E : A { + private constructor E() + internal final override /*1*/ tailrec /*fake_override*/ fun baa(/*0*/ y: kotlin.Int): kotlin.Unit + internal open override /*1*/ tailrec fun bar(/*0*/ y: kotlin.Int): kotlin.Unit + protected open override /*1*/ tailrec fun baz(/*0*/ y: kotlin.Int): kotlin.Unit + invisible_fake final override /*1*/ tailrec /*fake_override*/ fun boo(/*0*/ y: kotlin.Int): kotlin.Unit + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*1*/ tailrec fun foo(/*0*/ x: 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 E1 : E { + public constructor E1() + internal final override /*1*/ tailrec /*fake_override*/ fun baa(/*0*/ y: kotlin.Int): kotlin.Unit + internal open override /*1*/ tailrec fun bar(/*0*/ y: kotlin.Int): kotlin.Unit + protected open override /*1*/ tailrec fun baz(/*0*/ y: kotlin.Int): kotlin.Unit + invisible_fake final override /*1*/ tailrec /*fake_override*/ fun boo(/*0*/ y: kotlin.Int): kotlin.Unit + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*1*/ tailrec fun foo(/*0*/ x: 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 enum class F : kotlin.Enum { + enum entry F0 + + enum entry F1 + + private constructor F() + public final override /*1*/ /*fake_override*/ val name: kotlin.String + public final override /*1*/ /*fake_override*/ val ordinal: kotlin.Int + internal final tailrec fun baa(/*0*/ y: kotlin.Int): kotlin.Unit + internal open tailrec fun bar(/*0*/ y: kotlin.Int): kotlin.Unit + protected open tailrec fun baz(/*0*/ y: kotlin.Int): kotlin.Unit + private final tailrec fun boo(/*0*/ y: kotlin.Int): kotlin.Unit + protected final override /*1*/ /*fake_override*/ fun clone(): kotlin.Any + public final override /*1*/ /*fake_override*/ fun compareTo(/*0*/ other: F): 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 open tailrec fun foo(/*0*/ x: kotlin.Int): kotlin.Unit + public final override /*1*/ /*fake_override*/ /*isHiddenForResolutionEverywhereBesideSupercalls*/ fun getDeclaringClass(): java.lang.Class! + public final override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String + + // Static members + public final /*synthesized*/ fun valueOf(/*0*/ value: kotlin.String): F + public final /*synthesized*/ fun values(): kotlin.Array +} + +public final enum class G : kotlin.Enum { + enum entry G1 + + private constructor G() + public final override /*1*/ /*fake_override*/ val name: kotlin.String + public final override /*1*/ /*fake_override*/ val ordinal: kotlin.Int + internal final tailrec fun baa(/*0*/ y: kotlin.Int): kotlin.Unit + internal open tailrec fun bar(/*0*/ y: kotlin.Int): kotlin.Unit + protected open tailrec fun baz(/*0*/ y: kotlin.Int): kotlin.Unit + private final tailrec fun boo(/*0*/ y: kotlin.Int): kotlin.Unit + protected final override /*1*/ /*fake_override*/ fun clone(): kotlin.Any + public final override /*1*/ /*fake_override*/ fun compareTo(/*0*/ other: G): 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 open tailrec fun foo(/*0*/ x: kotlin.Int): kotlin.Unit + public final override /*1*/ /*fake_override*/ /*isHiddenForResolutionEverywhereBesideSupercalls*/ fun getDeclaringClass(): java.lang.Class! + public final override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String + + // Static members + public final /*synthesized*/ fun valueOf(/*0*/ value: kotlin.String): G + public final /*synthesized*/ fun values(): kotlin.Array +} diff --git a/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java index d6c83757e1d..dc7edf4d6bd 100644 --- a/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java @@ -611,6 +611,16 @@ public class DiagnosticsTestGenerated extends AbstractDiagnosticsTest { runTest("compiler/testData/diagnostics/tests/SyntaxErrorInTestHighlightingEof.kt"); } + @TestMetadata("tailRecOnVirtualMember.kt") + public void testTailRecOnVirtualMember() throws Exception { + runTest("compiler/testData/diagnostics/tests/tailRecOnVirtualMember.kt"); + } + + @TestMetadata("tailRecOnVirtualMemberError.kt") + public void testTailRecOnVirtualMemberError() throws Exception { + runTest("compiler/testData/diagnostics/tests/tailRecOnVirtualMemberError.kt"); + } + @TestMetadata("tailRecOverridden.kt") public void testTailRecOverridden() throws Exception { runTest("compiler/testData/diagnostics/tests/tailRecOverridden.kt"); diff --git a/compiler/tests/org/jetbrains/kotlin/checkers/javac/DiagnosticsUsingJavacTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/checkers/javac/DiagnosticsUsingJavacTestGenerated.java index 0d0effaad90..e5e916d52bf 100644 --- a/compiler/tests/org/jetbrains/kotlin/checkers/javac/DiagnosticsUsingJavacTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/checkers/javac/DiagnosticsUsingJavacTestGenerated.java @@ -611,6 +611,16 @@ public class DiagnosticsUsingJavacTestGenerated extends AbstractDiagnosticsUsing runTest("compiler/testData/diagnostics/tests/SyntaxErrorInTestHighlightingEof.kt"); } + @TestMetadata("tailRecOnVirtualMember.kt") + public void testTailRecOnVirtualMember() throws Exception { + runTest("compiler/testData/diagnostics/tests/tailRecOnVirtualMember.kt"); + } + + @TestMetadata("tailRecOnVirtualMemberError.kt") + public void testTailRecOnVirtualMemberError() throws Exception { + runTest("compiler/testData/diagnostics/tests/tailRecOnVirtualMemberError.kt"); + } + @TestMetadata("tailRecOverridden.kt") public void testTailRecOverridden() throws Exception { runTest("compiler/testData/diagnostics/tests/tailRecOverridden.kt"); diff --git a/compiler/util/src/org/jetbrains/kotlin/config/LanguageVersionSettings.kt b/compiler/util/src/org/jetbrains/kotlin/config/LanguageVersionSettings.kt index bc807c585d8..8dba573d2d3 100644 --- a/compiler/util/src/org/jetbrains/kotlin/config/LanguageVersionSettings.kt +++ b/compiler/util/src/org/jetbrains/kotlin/config/LanguageVersionSettings.kt @@ -108,6 +108,7 @@ enum class LanguageFeature( UseGetterNameForPropertyAnnotationsMethodOnJvm(KOTLIN_1_4), AllowBreakAndContinueInsideWhen(KOTLIN_1_4), MixedNamedArgumentsInTheirOwnPosition(KOTLIN_1_4), + ProhibitTailrecOnVirtualMember(KOTLIN_1_4, kind = BUG_FIX), ProperVisibilityForCompanionObjectInstanceField(sinceVersion = null, kind = BUG_FIX), // Temporarily disabled, see KT-27084/KT-22379