diff --git a/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirOldFrontendDiagnosticsTestGenerated.java b/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirOldFrontendDiagnosticsTestGenerated.java index f87bd495ea0..4acd3ef5722 100644 --- a/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirOldFrontendDiagnosticsTestGenerated.java +++ b/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirOldFrontendDiagnosticsTestGenerated.java @@ -770,6 +770,24 @@ public class FirOldFrontendDiagnosticsTestGenerated extends AbstractFirDiagnosti runTest("compiler/testData/diagnostics/tests/SyntaxErrorInTestHighlightingEof.kt"); } + @Test + @TestMetadata("tailRecBasic.kt") + public void testTailRecBasic() throws Exception { + runTest("compiler/testData/diagnostics/tests/tailRecBasic.kt"); + } + + @Test + @TestMetadata("tailRecInNestedScopes.kt") + public void testTailRecInNestedScopes() throws Exception { + runTest("compiler/testData/diagnostics/tests/tailRecInNestedScopes.kt"); + } + + @Test + @TestMetadata("tailRecInTry.kt") + public void testTailRecInTry() throws Exception { + runTest("compiler/testData/diagnostics/tests/tailRecInTry.kt"); + } + @Test @TestMetadata("tailRecOnVirtualMember.kt") public void testTailRecOnVirtualMember() throws Exception { @@ -788,6 +806,24 @@ public class FirOldFrontendDiagnosticsTestGenerated extends AbstractFirDiagnosti runTest("compiler/testData/diagnostics/tests/tailRecOverridden.kt"); } + @Test + @TestMetadata("tailRecSingleton.kt") + public void testTailRecSingleton() throws Exception { + runTest("compiler/testData/diagnostics/tests/tailRecSingleton.kt"); + } + + @Test + @TestMetadata("tailRecWithDispatchReceiver.kt") + public void testTailRecWithDispatchReceiver() throws Exception { + runTest("compiler/testData/diagnostics/tests/tailRecWithDispatchReceiver.kt"); + } + + @Test + @TestMetadata("tailRecWithExtensionReceiver.kt") + public void testTailRecWithExtensionReceiver() throws Exception { + runTest("compiler/testData/diagnostics/tests/tailRecWithExtensionReceiver.kt"); + } + @Test @TestMetadata("tailRecursionComplex.kt") public void testTailRecursionComplex() throws Exception { diff --git a/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirOldFrontendDiagnosticsWithLightTreeTestGenerated.java b/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirOldFrontendDiagnosticsWithLightTreeTestGenerated.java index 08c91323276..04a8945cec9 100644 --- a/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirOldFrontendDiagnosticsWithLightTreeTestGenerated.java +++ b/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirOldFrontendDiagnosticsWithLightTreeTestGenerated.java @@ -770,6 +770,24 @@ public class FirOldFrontendDiagnosticsWithLightTreeTestGenerated extends Abstrac runTest("compiler/testData/diagnostics/tests/SyntaxErrorInTestHighlightingEof.kt"); } + @Test + @TestMetadata("tailRecBasic.kt") + public void testTailRecBasic() throws Exception { + runTest("compiler/testData/diagnostics/tests/tailRecBasic.kt"); + } + + @Test + @TestMetadata("tailRecInNestedScopes.kt") + public void testTailRecInNestedScopes() throws Exception { + runTest("compiler/testData/diagnostics/tests/tailRecInNestedScopes.kt"); + } + + @Test + @TestMetadata("tailRecInTry.kt") + public void testTailRecInTry() throws Exception { + runTest("compiler/testData/diagnostics/tests/tailRecInTry.kt"); + } + @Test @TestMetadata("tailRecOnVirtualMember.kt") public void testTailRecOnVirtualMember() throws Exception { @@ -788,6 +806,24 @@ public class FirOldFrontendDiagnosticsWithLightTreeTestGenerated extends Abstrac runTest("compiler/testData/diagnostics/tests/tailRecOverridden.kt"); } + @Test + @TestMetadata("tailRecSingleton.kt") + public void testTailRecSingleton() throws Exception { + runTest("compiler/testData/diagnostics/tests/tailRecSingleton.kt"); + } + + @Test + @TestMetadata("tailRecWithDispatchReceiver.kt") + public void testTailRecWithDispatchReceiver() throws Exception { + runTest("compiler/testData/diagnostics/tests/tailRecWithDispatchReceiver.kt"); + } + + @Test + @TestMetadata("tailRecWithExtensionReceiver.kt") + public void testTailRecWithExtensionReceiver() throws Exception { + runTest("compiler/testData/diagnostics/tests/tailRecWithExtensionReceiver.kt"); + } + @Test @TestMetadata("tailRecursionComplex.kt") public void testTailRecursionComplex() throws Exception { diff --git a/compiler/fir/checkers/checkers-component-generator/src/org/jetbrains/kotlin/fir/checkers/generator/diagnostics/FirDiagnosticsList.kt b/compiler/fir/checkers/checkers-component-generator/src/org/jetbrains/kotlin/fir/checkers/generator/diagnostics/FirDiagnosticsList.kt index 4f611e1f2cc..91f938ac0a9 100644 --- a/compiler/fir/checkers/checkers-component-generator/src/org/jetbrains/kotlin/fir/checkers/generator/diagnostics/FirDiagnosticsList.kt +++ b/compiler/fir/checkers/checkers-component-generator/src/org/jetbrains/kotlin/fir/checkers/generator/diagnostics/FirDiagnosticsList.kt @@ -809,6 +809,11 @@ object DIAGNOSTICS_LIST : DiagnosticList("FirErrors") { // TODO: replace with KtParameter val CANNOT_INFER_PARAMETER_TYPE by error() + + val NO_TAIL_CALLS_FOUND by warning(PositioningStrategy.DECLARATION_SIGNATURE) + val TAILREC_ON_VIRTUAL_MEMBER_ERROR by error(PositioningStrategy.DECLARATION_SIGNATURE) + val NON_TAIL_RECURSIVE_CALL by warning(PositioningStrategy.REFERENCED_NAME_BY_QUALIFIED) + val TAIL_RECURSION_IN_TRY_IS_NOT_SUPPORTED by warning(PositioningStrategy.REFERENCED_NAME_BY_QUALIFIED) } val FUN_INTERFACES by object : DiagnosticGroup("Fun interfaces") { diff --git a/compiler/fir/checkers/gen/org/jetbrains/kotlin/fir/analysis/diagnostics/FirErrors.kt b/compiler/fir/checkers/gen/org/jetbrains/kotlin/fir/analysis/diagnostics/FirErrors.kt index bda4e94b0ad..344fbecb12e 100644 --- a/compiler/fir/checkers/gen/org/jetbrains/kotlin/fir/analysis/diagnostics/FirErrors.kt +++ b/compiler/fir/checkers/gen/org/jetbrains/kotlin/fir/analysis/diagnostics/FirErrors.kt @@ -66,6 +66,7 @@ import org.jetbrains.kotlin.psi.KtImportDirective import org.jetbrains.kotlin.psi.KtModifierListOwner import org.jetbrains.kotlin.psi.KtNameReferenceExpression import org.jetbrains.kotlin.psi.KtNamedDeclaration +import org.jetbrains.kotlin.psi.KtNamedFunction import org.jetbrains.kotlin.psi.KtObjectDeclaration import org.jetbrains.kotlin.psi.KtParameter import org.jetbrains.kotlin.psi.KtPrimaryConstructor @@ -438,6 +439,10 @@ object FirErrors { val FORBIDDEN_VARARG_PARAMETER_TYPE by error1(SourceElementPositioningStrategies.PARAMETER_VARARG_MODIFIER) val VALUE_PARAMETER_WITH_NO_TYPE_ANNOTATION by error0() val CANNOT_INFER_PARAMETER_TYPE by error0() + val NO_TAIL_CALLS_FOUND by warning0(SourceElementPositioningStrategies.DECLARATION_SIGNATURE) + val TAILREC_ON_VIRTUAL_MEMBER_ERROR by error0(SourceElementPositioningStrategies.DECLARATION_SIGNATURE) + val NON_TAIL_RECURSIVE_CALL by warning0(SourceElementPositioningStrategies.REFERENCED_NAME_BY_QUALIFIED) + val TAIL_RECURSION_IN_TRY_IS_NOT_SUPPORTED by warning0(SourceElementPositioningStrategies.REFERENCED_NAME_BY_QUALIFIED) // Fun interfaces val FUN_INTERFACE_CONSTRUCTOR_REFERENCE by error0(SourceElementPositioningStrategies.REFERENCE_BY_QUALIFIED) diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/CommonDeclarationCheckers.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/CommonDeclarationCheckers.kt index 43304933186..22302c60539 100644 --- a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/CommonDeclarationCheckers.kt +++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/CommonDeclarationCheckers.kt @@ -43,6 +43,7 @@ object CommonDeclarationCheckers : DeclarationCheckers() { FirFunctionNameChecker, FirFunctionTypeParametersSyntaxChecker, FirOperatorModifierChecker, + FirTailrecFunctionChecker, ) override val propertyCheckers: Set diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/declaration/FirDeclarationCheckerUtils.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/declaration/FirDeclarationCheckerUtils.kt index f6f0a0cc278..aae9b5d456c 100644 --- a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/declaration/FirDeclarationCheckerUtils.kt +++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/declaration/FirDeclarationCheckerUtils.kt @@ -45,6 +45,16 @@ private inline fun isInsideSpecificClass( context.containingDeclarations.asReversed().any { it is FirRegularClass && predicate.invoke(it) } } +internal fun FirMemberDeclaration.isEffectivelyFinal(context: CheckerContext): Boolean { + if (this.isFinal) return true + val containingClass = context.containingDeclarations.lastOrNull() as? FirRegularClass ?: return true + if (containingClass.isEnumClass) { + // Enum class has enum entries and hence is not considered final. + return false + } + return containingClass.isFinal +} + internal fun FirMemberDeclaration.isEffectivelyExpect( containingClass: FirClass?, context: CheckerContext, diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/declaration/FirTailrecFunctionChecker.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/declaration/FirTailrecFunctionChecker.kt new file mode 100644 index 00000000000..873a1e1f3a2 --- /dev/null +++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/declaration/FirTailrecFunctionChecker.kt @@ -0,0 +1,132 @@ +/* + * Copyright 2010-2021 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.fir.analysis.checkers.declaration + +import org.jetbrains.kotlin.fir.analysis.cfa.TraverseDirection +import org.jetbrains.kotlin.fir.analysis.cfa.traverse +import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext +import org.jetbrains.kotlin.fir.analysis.diagnostics.DiagnosticReporter +import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors +import org.jetbrains.kotlin.fir.analysis.diagnostics.reportOn +import org.jetbrains.kotlin.fir.declarations.FirSimpleFunction +import org.jetbrains.kotlin.fir.declarations.utils.isOverride +import org.jetbrains.kotlin.fir.declarations.utils.isTailRec +import org.jetbrains.kotlin.fir.expressions.FirThisReceiverExpression +import org.jetbrains.kotlin.fir.expressions.arguments +import org.jetbrains.kotlin.fir.expressions.impl.FirNoReceiverExpression +import org.jetbrains.kotlin.fir.expressions.toResolvedCallableSymbol +import org.jetbrains.kotlin.fir.resolve.dfa.cfg.* +import org.jetbrains.kotlin.fir.resolve.dfa.controlFlowGraph +import org.jetbrains.kotlin.fir.symbols.impl.FirNamedFunctionSymbol + +object FirTailrecFunctionChecker : FirSimpleFunctionChecker() { + override fun check(declaration: FirSimpleFunction, context: CheckerContext, reporter: DiagnosticReporter) { + if (!declaration.isTailRec) return + if (!declaration.isEffectivelyFinal(context)) { + reporter.reportOn(declaration.source, FirErrors.TAILREC_ON_VIRTUAL_MEMBER_ERROR, context) + } + val graph = declaration.controlFlowGraphReference?.controlFlowGraph ?: return + + var tryScopeCount = 0 + var catchScopeCount = 0 + var finallyScopeCount = 0 + var tailrecCount = 0 + var lambdaScopeCount = 0 + graph.traverse(TraverseDirection.Forward, object : ControlFlowGraphVisitorVoid() { + override fun visitNode(node: CFGNode<*>) {} + override fun visitPostponedLambdaEnterNode(node: PostponedLambdaEnterNode) { + lambdaScopeCount++ + } + + override fun visitPostponedLambdaExitNode(node: PostponedLambdaExitNode) { + lambdaScopeCount-- + } + + override fun visitTryMainBlockEnterNode(node: TryMainBlockEnterNode) { + tryScopeCount++ + } + + override fun visitTryMainBlockExitNode(node: TryMainBlockExitNode) { + tryScopeCount-- + } + + override fun visitCatchClauseEnterNode(node: CatchClauseEnterNode) { + catchScopeCount++ + } + + override fun visitCatchClauseExitNode(node: CatchClauseExitNode) { + catchScopeCount-- + } + + override fun visitFinallyBlockEnterNode(node: FinallyBlockEnterNode) { + finallyScopeCount++ + } + + override fun visitFinallyBlockExitNode(node: FinallyBlockExitNode) { + finallyScopeCount-- + } + + override fun visitFunctionCallNode(node: FunctionCallNode) { + val functionCall = node.fir + val resolvedSymbol = functionCall.calleeReference.toResolvedCallableSymbol() as? FirNamedFunctionSymbol ?: return + if (resolvedSymbol != declaration.symbol) return + if (functionCall.arguments.size != resolvedSymbol.valueParameterSymbols.size && resolvedSymbol.isOverride) { + // Overridden functions using default arguments at tail call are not included: KT-4285 + reporter.reportOn(functionCall.source, FirErrors.NON_TAIL_RECURSIVE_CALL, context) + return + } + val dispatchReceiver = functionCall.dispatchReceiver + // A tailrec call does not support changing dispatchers. Here we report changing dispatch receiver if the dispatch receiver + // is present and not a `this`. For the `this` check, we don't need to actually compare if the dispatch receiver `this` + // references the same `this` made available from `declaration`. This is because + // 1. if `this` is not labeled, then it references the innermost `this` receiver. If the innermost scope is not the + // `declaration` body, then follow-up checks on following nodes would report there to be more instructions, which would + // then make this call non-tailrec. + // 2. If `this` is labeled, then one of the following is possible. + // a. the call is in some context that has additional implicit `this` declared. But this can only happen if the call is + // placed inside some extension lambda, which would be covered by the later check on exiting node. + // b. `declaration` is a member function in a local class and the receiver is a labeled `this` pointing to the outer + // non-local class. In this case, the resolved symbol cannot be the same as the symbol of `declaration`, and this case + // is already bailed out earlier. So there is no need to report anything. + // c. `declaration` is a member function of an inner class and the receiver is a labeled `this` pointing to the outer + // class. The reasoning is the same with b. + // Also note that if the dispatch receiver is explicitly specify to be a singleton, the call is not compiled as a tailrec + // either. See KT-48602. + if (dispatchReceiver !is FirThisReceiverExpression && dispatchReceiver !is FirNoReceiverExpression) { + reporter.reportOn(functionCall.source, FirErrors.NON_TAIL_RECURSIVE_CALL, context) + return + } + if (tryScopeCount > 0 || catchScopeCount > 0 || finallyScopeCount > 0) { + reporter.reportOn(functionCall.source, FirErrors.TAIL_RECURSION_IN_TRY_IS_NOT_SUPPORTED, context) + } else if (lambdaScopeCount > 0 || node.hasMoreFollowingInstructions(declaration)) { + reporter.reportOn(functionCall.source, FirErrors.NON_TAIL_RECURSIVE_CALL, context) + } else if (!node.isDead) { + tailrecCount++ + } + } + }) + if (tailrecCount == 0) { + reporter.reportOn(declaration.source, FirErrors.NO_TAIL_CALLS_FOUND, context) + } + } + + private fun CFGNode<*>.hasMoreFollowingInstructions(tailrecFunction: FirSimpleFunction): Boolean { + for (next in followingNodes) { + val edge = outgoingEdges.getValue(next) + if (!edge.kind.usedInCfa || edge.kind.isDead) continue + if (edge.kind.isBack) return true + val hasMore = when (next) { + // If exiting another function, then it means this call is inside a nested local function, in which case, it's not a tailrec call. + is FunctionExitNode -> return next.fir != tailrecFunction + is JumpNode, is BinaryAndExitNode, is BinaryOrExitNode, is WhenBranchResultExitNode, is WhenExitNode, is BlockExitNode -> + next.hasMoreFollowingInstructions(tailrecFunction) + else -> return true + } + if (hasMore) return hasMore + } + return false + } +} \ No newline at end of file diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/diagnostics/FirDefaultErrorMessages.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/diagnostics/FirDefaultErrorMessages.kt index c03841d4770..f5dc77259e0 100644 --- a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/diagnostics/FirDefaultErrorMessages.kt +++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/diagnostics/FirDefaultErrorMessages.kt @@ -298,6 +298,7 @@ import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.NON_MODIFIER_FORM import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.NON_PRIVATE_CONSTRUCTOR_IN_ENUM import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.NON_PRIVATE_OR_PROTECTED_CONSTRUCTOR_IN_SEALED import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.NON_PUBLIC_CALL_FROM_PUBLIC_INLINE +import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.NON_TAIL_RECURSIVE_CALL import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.NON_VARARG_SPREAD import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.NOTHING_TO_INLINE import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.NOTHING_TO_OVERRIDE @@ -315,6 +316,7 @@ import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.NO_GET_METHOD import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.NO_RECEIVER_ALLOWED import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.NO_RETURN_IN_FUNCTION_WITH_BLOCK_BODY import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.NO_SET_METHOD +import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.NO_TAIL_CALLS_FOUND import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.NO_THIS import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.NO_VALUE_FOR_PARAMETER import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.NULLABLE_INLINE_PARAMETER @@ -418,6 +420,8 @@ import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.SUPER_CALL_FROM_P import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.SUPER_IS_NOT_AN_EXPRESSION import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.SUPER_NOT_AVAILABLE import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.SYNTAX +import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.TAILREC_ON_VIRTUAL_MEMBER_ERROR +import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.TAIL_RECURSION_IN_TRY_IS_NOT_SUPPORTED import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.THROWABLE_TYPE_MISMATCH import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.TOO_MANY_ARGUMENTS import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.TOPLEVEL_TYPEALIASES_ONLY @@ -1218,6 +1222,10 @@ class FirDefaultErrorMessages { map.put(FORBIDDEN_VARARG_PARAMETER_TYPE, "Forbidden vararg parameter type: {0}", RENDER_TYPE) map.put(VALUE_PARAMETER_WITH_NO_TYPE_ANNOTATION, "A type annotation is required on a value parameter") map.put(CANNOT_INFER_PARAMETER_TYPE, "cannot infer a type for this parameter. Please specify it explicitly.") + 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_ERROR, "Tailrec is not allowed on open members"); + map.put(NON_TAIL_RECURSIVE_CALL, "Recursive call is not a tail call"); + map.put(TAIL_RECURSION_IN_TRY_IS_NOT_SUPPORTED, "Tail recursion optimization inside try/catch/finally is not supported"); // Fun interfaces map.put(FUN_INTERFACE_CONSTRUCTOR_REFERENCE, "Functional/SAM interface constructor references are prohibited") diff --git a/compiler/testData/codegen/box/diagnostics/functions/tailRecursion/functionWithNoTails.kt b/compiler/testData/codegen/box/diagnostics/functions/tailRecursion/functionWithNoTails.kt index d3184b5847a..21216f009af 100644 --- a/compiler/testData/codegen/box/diagnostics/functions/tailRecursion/functionWithNoTails.kt +++ b/compiler/testData/codegen/box/diagnostics/functions/tailRecursion/functionWithNoTails.kt @@ -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 noTails() { // nothing here diff --git a/compiler/testData/codegen/box/diagnostics/functions/tailRecursion/labeledThisReferences.kt b/compiler/testData/codegen/box/diagnostics/functions/tailRecursion/labeledThisReferences.kt index 17040b4417d..aa473b7a699 100644 --- a/compiler/testData/codegen/box/diagnostics/functions/tailRecursion/labeledThisReferences.kt +++ b/compiler/testData/codegen/box/diagnostics/functions/tailRecursion/labeledThisReferences.kt @@ -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 { diff --git a/compiler/testData/codegen/box/diagnostics/functions/tailRecursion/loops.kt b/compiler/testData/codegen/box/diagnostics/functions/tailRecursion/loops.kt index 5250576fa54..74960ee5255 100644 --- a/compiler/testData/codegen/box/diagnostics/functions/tailRecursion/loops.kt +++ b/compiler/testData/codegen/box/diagnostics/functions/tailRecursion/loops.kt @@ -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 diff --git a/compiler/testData/codegen/box/diagnostics/functions/tailRecursion/multilevelBlocks.kt b/compiler/testData/codegen/box/diagnostics/functions/tailRecursion/multilevelBlocks.kt index 273b0226628..761ff5ff7d9 100644 --- a/compiler/testData/codegen/box/diagnostics/functions/tailRecursion/multilevelBlocks.kt +++ b/compiler/testData/codegen/box/diagnostics/functions/tailRecursion/multilevelBlocks.kt @@ -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) { diff --git a/compiler/testData/codegen/box/diagnostics/functions/tailRecursion/recursiveCallInInlineLambda.kt b/compiler/testData/codegen/box/diagnostics/functions/tailRecursion/recursiveCallInInlineLambda.kt index a0a98d9058d..6c82ccdab20 100644 --- a/compiler/testData/codegen/box/diagnostics/functions/tailRecursion/recursiveCallInInlineLambda.kt +++ b/compiler/testData/codegen/box/diagnostics/functions/tailRecursion/recursiveCallInInlineLambda.kt @@ -1,5 +1,6 @@ // KT-16549 // IGNORE_BACKEND: JVM +// IGNORE_FIR_DIAGNOSTICS_DIFF class TailInline { private inline fun act(action: () -> Unit) { diff --git a/compiler/testData/codegen/box/diagnostics/functions/tailRecursion/recursiveCallInInlineLambdaWithCapture.kt b/compiler/testData/codegen/box/diagnostics/functions/tailRecursion/recursiveCallInInlineLambdaWithCapture.kt index 6b3da285c1c..997d58f5ff4 100644 --- a/compiler/testData/codegen/box/diagnostics/functions/tailRecursion/recursiveCallInInlineLambdaWithCapture.kt +++ b/compiler/testData/codegen/box/diagnostics/functions/tailRecursion/recursiveCallInInlineLambdaWithCapture.kt @@ -1,6 +1,7 @@ // KT-14961 // IGNORE_BACKEND: JVM, JS_IR, WASM // WITH_RUNTIME +// IGNORE_FIR_DIAGNOSTICS_DIFF fun listOfFactor(number: Int): List { tailrec fun listOfFactor(number: Int, acc: List): List { diff --git a/compiler/testData/codegen/box/diagnostics/functions/tailRecursion/recursiveCallInLambda.kt b/compiler/testData/codegen/box/diagnostics/functions/tailRecursion/recursiveCallInLambda.kt index edf0ff85c8b..265d12dcb27 100644 --- a/compiler/testData/codegen/box/diagnostics/functions/tailRecursion/recursiveCallInLambda.kt +++ b/compiler/testData/codegen/box/diagnostics/functions/tailRecursion/recursiveCallInLambda.kt @@ -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 foo() { bar { diff --git a/compiler/testData/codegen/box/diagnostics/functions/tailRecursion/recursiveCallInLocalFunction.kt b/compiler/testData/codegen/box/diagnostics/functions/tailRecursion/recursiveCallInLocalFunction.kt index cc39c8d2332..d96d03e22b4 100644 --- a/compiler/testData/codegen/box/diagnostics/functions/tailRecursion/recursiveCallInLocalFunction.kt +++ b/compiler/testData/codegen/box/diagnostics/functions/tailRecursion/recursiveCallInLocalFunction.kt @@ -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 foo() { fun bar() { diff --git a/compiler/testData/codegen/box/diagnostics/functions/tailRecursion/returnIf.kt b/compiler/testData/codegen/box/diagnostics/functions/tailRecursion/returnIf.kt index 62f50687301..31d315d4ba0 100644 --- a/compiler/testData/codegen/box/diagnostics/functions/tailRecursion/returnIf.kt +++ b/compiler/testData/codegen/box/diagnostics/functions/tailRecursion/returnIf.kt @@ -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) { diff --git a/compiler/testData/codegen/box/diagnostics/functions/tailRecursion/returnInCatch.kt b/compiler/testData/codegen/box/diagnostics/functions/tailRecursion/returnInCatch.kt index 89a6c01743f..a5e7c44a3ef 100644 --- a/compiler/testData/codegen/box/diagnostics/functions/tailRecursion/returnInCatch.kt +++ b/compiler/testData/codegen/box/diagnostics/functions/tailRecursion/returnInCatch.kt @@ -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(counter : Int) : Int { if (counter == 0) return 0 diff --git a/compiler/testData/codegen/box/diagnostics/functions/tailRecursion/returnInFinally.kt b/compiler/testData/codegen/box/diagnostics/functions/tailRecursion/returnInFinally.kt index fe5b34dcf0a..9d38733b072 100644 --- a/compiler/testData/codegen/box/diagnostics/functions/tailRecursion/returnInFinally.kt +++ b/compiler/testData/codegen/box/diagnostics/functions/tailRecursion/returnInFinally.kt @@ -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(counter : Int) : Int { if (counter == 0) return 0 diff --git a/compiler/testData/codegen/box/diagnostics/functions/tailRecursion/returnInIfInFinally.kt b/compiler/testData/codegen/box/diagnostics/functions/tailRecursion/returnInIfInFinally.kt index f32b1332912..30b5bb0200f 100644 --- a/compiler/testData/codegen/box/diagnostics/functions/tailRecursion/returnInIfInFinally.kt +++ b/compiler/testData/codegen/box/diagnostics/functions/tailRecursion/returnInIfInFinally.kt @@ -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(counter : Int) : Int { if (counter == 0) return 0 diff --git a/compiler/testData/codegen/box/diagnostics/functions/tailRecursion/returnInTry.kt b/compiler/testData/codegen/box/diagnostics/functions/tailRecursion/returnInTry.kt index 592e48a32ca..f83c8fe8aba 100644 --- a/compiler/testData/codegen/box/diagnostics/functions/tailRecursion/returnInTry.kt +++ b/compiler/testData/codegen/box/diagnostics/functions/tailRecursion/returnInTry.kt @@ -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(counter : Int) : Int { if (counter == 0) return 0 diff --git a/compiler/testData/codegen/box/diagnostics/functions/tailRecursion/simpleBlock.kt b/compiler/testData/codegen/box/diagnostics/functions/tailRecursion/simpleBlock.kt index 33c5b4f5689..9a158dcb486 100644 --- a/compiler/testData/codegen/box/diagnostics/functions/tailRecursion/simpleBlock.kt +++ b/compiler/testData/codegen/box/diagnostics/functions/tailRecursion/simpleBlock.kt @@ -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) { diff --git a/compiler/testData/codegen/box/diagnostics/functions/tailRecursion/simpleReturn.kt b/compiler/testData/codegen/box/diagnostics/functions/tailRecursion/simpleReturn.kt index 6b524b593c7..936b2ab66e6 100644 --- a/compiler/testData/codegen/box/diagnostics/functions/tailRecursion/simpleReturn.kt +++ b/compiler/testData/codegen/box/diagnostics/functions/tailRecursion/simpleReturn.kt @@ -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) { diff --git a/compiler/testData/codegen/box/diagnostics/functions/tailRecursion/simpleReturnWithElse.kt b/compiler/testData/codegen/box/diagnostics/functions/tailRecursion/simpleReturnWithElse.kt index 8a6e0cb4a7b..ac908be952e 100644 --- a/compiler/testData/codegen/box/diagnostics/functions/tailRecursion/simpleReturnWithElse.kt +++ b/compiler/testData/codegen/box/diagnostics/functions/tailRecursion/simpleReturnWithElse.kt @@ -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) { diff --git a/compiler/testData/codegen/box/diagnostics/functions/tailRecursion/tailRecursionInFinally.kt b/compiler/testData/codegen/box/diagnostics/functions/tailRecursion/tailRecursionInFinally.kt index 7acc3c55016..d77723ce2a2 100644 --- a/compiler/testData/codegen/box/diagnostics/functions/tailRecursion/tailRecursionInFinally.kt +++ b/compiler/testData/codegen/box/diagnostics/functions/tailRecursion/tailRecursionInFinally.kt @@ -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(go: Boolean) : Unit { if (!go) return diff --git a/compiler/testData/codegen/box/diagnostics/functions/tailRecursion/thisReferences.kt b/compiler/testData/codegen/box/diagnostics/functions/tailRecursion/thisReferences.kt index 130cae04858..36118a0ef54 100644 --- a/compiler/testData/codegen/box/diagnostics/functions/tailRecursion/thisReferences.kt +++ b/compiler/testData/codegen/box/diagnostics/functions/tailRecursion/thisReferences.kt @@ -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) { diff --git a/compiler/testData/codegen/box/diagnostics/functions/tailRecursion/unitBlocks.kt b/compiler/testData/codegen/box/diagnostics/functions/tailRecursion/unitBlocks.kt index 123c04f7b3e..7ae0c8f0e6a 100644 --- a/compiler/testData/codegen/box/diagnostics/functions/tailRecursion/unitBlocks.kt +++ b/compiler/testData/codegen/box/diagnostics/functions/tailRecursion/unitBlocks.kt @@ -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) { diff --git a/compiler/testData/codegen/box/diagnostics/functions/tailRecursion/whenWithCondition.kt b/compiler/testData/codegen/box/diagnostics/functions/tailRecursion/whenWithCondition.kt index 16aca4d143a..57f8e8e300e 100644 --- a/compiler/testData/codegen/box/diagnostics/functions/tailRecursion/whenWithCondition.kt +++ b/compiler/testData/codegen/box/diagnostics/functions/tailRecursion/whenWithCondition.kt @@ -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) { diff --git a/compiler/testData/codegen/box/diagnostics/functions/tailRecursion/whenWithInRange.kt b/compiler/testData/codegen/box/diagnostics/functions/tailRecursion/whenWithInRange.kt index dbf0e371eab..64791dd2561 100644 --- a/compiler/testData/codegen/box/diagnostics/functions/tailRecursion/whenWithInRange.kt +++ b/compiler/testData/codegen/box/diagnostics/functions/tailRecursion/whenWithInRange.kt @@ -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) { diff --git a/compiler/testData/codegen/box/diagnostics/functions/tailRecursion/whenWithoutCondition.kt b/compiler/testData/codegen/box/diagnostics/functions/tailRecursion/whenWithoutCondition.kt index 8aa3c35752f..b13233a929d 100644 --- a/compiler/testData/codegen/box/diagnostics/functions/tailRecursion/whenWithoutCondition.kt +++ b/compiler/testData/codegen/box/diagnostics/functions/tailRecursion/whenWithoutCondition.kt @@ -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 { diff --git a/compiler/testData/diagnostics/tests/tailRecBasic.kt b/compiler/testData/diagnostics/tests/tailRecBasic.kt new file mode 100644 index 00000000000..b74776a4612 --- /dev/null +++ b/compiler/testData/diagnostics/tests/tailRecBasic.kt @@ -0,0 +1,80 @@ +// FIR_IDENTICAL +// WITH_STDLIB +// !DIAGNOSTICS: -UNREACHABLE_CODE + +tailrec fun foo1() { + foo1() + 1 +} + +tailrec fun foo2() { + foo2() + val i = 1 +} + +tailrec fun foo3() { + foo3() + foo1() +} + +tailrec fun foo4() { + if (true) foo4() + else foo3() +} + +tailrec fun foo5() { + return foo5() + foo4() +} + +tailrec fun foo6(b: Boolean) { + while (b) { + foo6(!b) + } +} + +tailrec fun foo7_return() { + while (true) { + foo7_return() + return + } +} + +tailrec fun foo7_break() { + while (true) { + foo7_break() + break + } +} + +tailrec fun foo7_continue() { + while (true) { + foo7_continue() + continue + } +} + +tailrec fun foo8() { + while (true) { + foo8() + throw Exception() + } + foo8() +} + +tailrec fun foo9() { + foo9() + fun bar() {} +} + +tailrec fun foo10() { + foo10() + class Bar { + val i = 1 + } +} + +tailrec fun foo11(): String { + return "hello ${foo11()}" +} + diff --git a/compiler/testData/diagnostics/tests/tailRecBasic.txt b/compiler/testData/diagnostics/tests/tailRecBasic.txt new file mode 100644 index 00000000000..56f5e6c0f0e --- /dev/null +++ b/compiler/testData/diagnostics/tests/tailRecBasic.txt @@ -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 + diff --git a/compiler/testData/diagnostics/tests/tailRecInNestedScopes.fir.kt b/compiler/testData/diagnostics/tests/tailRecInNestedScopes.fir.kt new file mode 100644 index 00000000000..d8fbc3a6e7e --- /dev/null +++ b/compiler/testData/diagnostics/tests/tailRecInNestedScopes.fir.kt @@ -0,0 +1,37 @@ +// WITH_STDLIB + +tailrec fun foo1() { + run { + foo1() + } +} + +fun myRun(f: () -> Unit) = f() + + +tailrec fun foo2() { + myRun { + foo2() + } +} + +tailrec fun foo3() { + fun bar() { + foo3() + } + bar() +} + +class A { + tailrec fun foo4() { + with(this) { + foo4() + } + } +} + +tailrec fun foo5() { + run { + return foo5() + } +} diff --git a/compiler/testData/diagnostics/tests/tailRecInNestedScopes.kt b/compiler/testData/diagnostics/tests/tailRecInNestedScopes.kt new file mode 100644 index 00000000000..d194f974eb6 --- /dev/null +++ b/compiler/testData/diagnostics/tests/tailRecInNestedScopes.kt @@ -0,0 +1,37 @@ +// WITH_STDLIB + +tailrec fun foo1() { + run { + foo1() + } +} + +fun myRun(f: () -> Unit) = f() + + +tailrec fun foo2() { + myRun { + foo2() + } +} + +tailrec fun foo3() { + fun bar() { + foo3() + } + bar() +} + +class A { + tailrec fun foo4() { + with(this) { + foo4() + } + } +} + +tailrec fun foo5() { + run { + return foo5() + } +} diff --git a/compiler/testData/diagnostics/tests/tailRecInNestedScopes.txt b/compiler/testData/diagnostics/tests/tailRecInNestedScopes.txt new file mode 100644 index 00000000000..57ed1efd5c6 --- /dev/null +++ b/compiler/testData/diagnostics/tests/tailRecInNestedScopes.txt @@ -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 +} + diff --git a/compiler/testData/diagnostics/tests/tailRecInTry.kt b/compiler/testData/diagnostics/tests/tailRecInTry.kt new file mode 100644 index 00000000000..6ea81da3bdb --- /dev/null +++ b/compiler/testData/diagnostics/tests/tailRecInTry.kt @@ -0,0 +1,64 @@ +// FIR_IDENTICAL +tailrec fun foo1() { + try { + foo1() + } catch (e: Exception) { + foo1() + } finally { + foo1() + } +} + +tailrec fun foo2() { + try { + foo2() + foo1() + } catch (e: Exception) { + foo2() + foo1() + } finally { + foo2() + foo1() + } +} + +tailrec fun foo3() { + try { + try { + foo3() + } finally { + } + } catch (e: Exception) { + try { + foo3() + } finally { + } + } finally { + try { + foo3() + } finally { + } + } +} + +tailrec fun foo4() { + try { + if (true) { + foo4() + } else { + foo1() + } + } catch (e: Exception) { + if (true) { + foo4() + } else { + foo1() + } + } finally { + if (true) { + foo4() + } else { + foo1() + } + } +} diff --git a/compiler/testData/diagnostics/tests/tailRecInTry.txt b/compiler/testData/diagnostics/tests/tailRecInTry.txt new file mode 100644 index 00000000000..9341eed7fa7 --- /dev/null +++ b/compiler/testData/diagnostics/tests/tailRecInTry.txt @@ -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 + diff --git a/compiler/testData/diagnostics/tests/tailRecOnVirtualMember.fir.kt b/compiler/testData/diagnostics/tests/tailRecOnVirtualMember.fir.kt index 12e7f3eb990..2fce7da07d6 100644 --- a/compiler/testData/diagnostics/tests/tailRecOnVirtualMember.fir.kt +++ b/compiler/testData/diagnostics/tests/tailRecOnVirtualMember.fir.kt @@ -1,15 +1,15 @@ //!LANGUAGE: -ProhibitTailrecOnVirtualMember open class A { - tailrec open fun foo(x: Int) { + tailrec open fun foo(x: Int) { foo(x) } - internal tailrec open fun bar(y: Int) { + internal tailrec open fun bar(y: Int) { bar(y) } - protected tailrec open fun baz(y: Int) { + 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 override fun foo(x: Int) { foo(x) } - tailrec override fun bar(y: Int) { + tailrec override fun bar(y: Int) { bar(y) } - tailrec override fun baz(y: Int) { + 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 override fun foo(x: Int) { foo(x) } - tailrec override fun bar(y: Int) { + tailrec override fun bar(y: Int) { bar(y) } - tailrec override fun baz(y: Int) { + tailrec override fun baz(y: Int) { baz(y) } @@ -109,15 +109,15 @@ enum class F { } }; - tailrec open fun foo(x: Int) { + tailrec open fun foo(x: Int) { foo(x) } - internal tailrec open fun bar(y: Int) { + internal tailrec open fun bar(y: Int) { bar(y) } - protected tailrec open fun baz(y: Int) { + protected tailrec open fun baz(y: Int) { baz(y) } @@ -134,15 +134,15 @@ enum class G { G1; - tailrec open fun foo(x: Int) { + tailrec open fun foo(x: Int) { foo(x) } - internal tailrec open fun bar(y: Int) { + internal tailrec open fun bar(y: Int) { bar(y) } - protected tailrec open fun baz(y: Int) { + protected tailrec open fun baz(y: Int) { baz(y) } diff --git a/compiler/testData/diagnostics/tests/tailRecOnVirtualMemberError.fir.kt b/compiler/testData/diagnostics/tests/tailRecOnVirtualMemberError.fir.kt deleted file mode 100644 index c11063ead15..00000000000 --- a/compiler/testData/diagnostics/tests/tailRecOnVirtualMemberError.fir.kt +++ /dev/null @@ -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) - } -} \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/tailRecOnVirtualMemberError.kt b/compiler/testData/diagnostics/tests/tailRecOnVirtualMemberError.kt index 1ccdbd6ea1a..a9e506fc898 100644 --- a/compiler/testData/diagnostics/tests/tailRecOnVirtualMemberError.kt +++ b/compiler/testData/diagnostics/tests/tailRecOnVirtualMemberError.kt @@ -1,3 +1,4 @@ +// FIR_IDENTICAL //!LANGUAGE: +ProhibitTailrecOnVirtualMember open class A { diff --git a/compiler/testData/diagnostics/tests/tailRecOverridden.fir.kt b/compiler/testData/diagnostics/tests/tailRecOverridden.fir.kt index 67f32ce524f..19e2f660a59 100644 --- a/compiler/testData/diagnostics/tests/tailRecOverridden.fir.kt +++ b/compiler/testData/diagnostics/tests/tailRecOverridden.fir.kt @@ -6,12 +6,12 @@ open class A { } class B: A() { - tailrec override fun foo(x: Int) { - foo() + tailrec override fun foo(x: Int) { + foo() } - tailrec override fun gav(y: Int, z: Int) { - gav(y) + tailrec override fun gav(y: Int, z: Int) { + 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) } -} \ No newline at end of file +} diff --git a/compiler/testData/diagnostics/tests/tailRecSingleton.fir.kt b/compiler/testData/diagnostics/tests/tailRecSingleton.fir.kt new file mode 100644 index 00000000000..79e7aa3ae55 --- /dev/null +++ b/compiler/testData/diagnostics/tests/tailRecSingleton.fir.kt @@ -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() + } + + tailrec fun bar3() { + Bar.bar3() + } + + tailrec fun bar4() { + Bar.Companion.bar4() + } + } +} + +enum class E { + A { + override tailrec fun rec() { + rec() + } + }, + B { + override tailrec fun rec() { + this.rec() + } + }, + C { + override tailrec fun rec() { + C.rec() // resolution goes to `E.rec`. Hence the resolved symbol is considered different from `C.rec`. + } + }; + + abstract fun rec() +} diff --git a/compiler/testData/diagnostics/tests/tailRecSingleton.kt b/compiler/testData/diagnostics/tests/tailRecSingleton.kt new file mode 100644 index 00000000000..53d229bdc1c --- /dev/null +++ b/compiler/testData/diagnostics/tests/tailRecSingleton.kt @@ -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() + } + + tailrec fun bar3() { + Bar.bar3() + } + + tailrec fun bar4() { + Bar.Companion.bar4() + } + } +} + +enum class E { + A { + override tailrec fun rec() { + rec() + } + }, + B { + override tailrec fun rec() { + this.rec() + } + }, + C { + override tailrec fun rec() { + C.rec() // resolution goes to `E.rec`. Hence the resolved symbol is considered different from `C.rec`. + } + }; + + abstract fun rec() +} diff --git a/compiler/testData/diagnostics/tests/tailRecSingleton.txt b/compiler/testData/diagnostics/tests/tailRecSingleton.txt new file mode 100644 index 00000000000..d5632b48900 --- /dev/null +++ b/compiler/testData/diagnostics/tests/tailRecSingleton.txt @@ -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 { + 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! + 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 +} + +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 +} + diff --git a/compiler/testData/diagnostics/tests/tailRecWithDispatchReceiver.kt b/compiler/testData/diagnostics/tests/tailRecWithDispatchReceiver.kt new file mode 100644 index 00000000000..82295b290dd --- /dev/null +++ b/compiler/testData/diagnostics/tests/tailRecWithDispatchReceiver.kt @@ -0,0 +1,21 @@ +// FIR_IDENTICAL +class A(val a: A) { + tailrec fun foo1() { + a.foo1() + } + + tailrec fun foo2() { + this.foo2() + } + + tailrec fun foo3() { + foo3() + } + + tailrec fun foo4() { + with(a) { + foo4() + return + } + } +} diff --git a/compiler/testData/diagnostics/tests/tailRecWithDispatchReceiver.txt b/compiler/testData/diagnostics/tests/tailRecWithDispatchReceiver.txt new file mode 100644 index 00000000000..85b8bcca09c --- /dev/null +++ b/compiler/testData/diagnostics/tests/tailRecWithDispatchReceiver.txt @@ -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 +} + diff --git a/compiler/testData/diagnostics/tests/tailRecWithExtensionReceiver.kt b/compiler/testData/diagnostics/tests/tailRecWithExtensionReceiver.kt new file mode 100644 index 00000000000..d0140345786 --- /dev/null +++ b/compiler/testData/diagnostics/tests/tailRecWithExtensionReceiver.kt @@ -0,0 +1,14 @@ +// FIR_IDENTICAL +tailrec fun String.foo1() { + "".foo1() +} + +tailrec fun String.foo2() { + this.foo2() +} + +tailrec fun String.foo3() { + with(this) { + foo3() + } +} diff --git a/compiler/testData/diagnostics/tests/tailRecWithExtensionReceiver.txt b/compiler/testData/diagnostics/tests/tailRecWithExtensionReceiver.txt new file mode 100644 index 00000000000..b6bcacc773c --- /dev/null +++ b/compiler/testData/diagnostics/tests/tailRecWithExtensionReceiver.txt @@ -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 diff --git a/compiler/testData/diagnostics/tests/tailRecursionComplex.fir.kt b/compiler/testData/diagnostics/tests/tailRecursionComplex.fir.kt index 7fc93d8ff12..df08de4612f 100644 --- a/compiler/testData/diagnostics/tests/tailRecursionComplex.fir.kt +++ b/compiler/testData/diagnostics/tests/tailRecursionComplex.fir.kt @@ -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 + tailrec fun foo(i: Int): Int = if (i < 0) 0 else O.foo(i - 1) } class A { - tailrec fun foo(i: Int) = if (i < 0) 0 else A.foo(i - 1) + 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 newline at end of file + tailrec fun foo(i: Int) = if (i < 0) 0 else O.foo(i - 1) +} diff --git a/compiler/testData/diagnostics/tests/tailRecursionComplex.kt b/compiler/testData/diagnostics/tests/tailRecursionComplex.kt index 9569b51651c..865d6e0bd72 100644 --- a/compiler/testData/diagnostics/tests/tailRecursionComplex.kt +++ b/compiler/testData/diagnostics/tests/tailRecursionComplex.kt @@ -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) } diff --git a/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/DiagnosticTestGenerated.java b/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/DiagnosticTestGenerated.java index 976aac48fa1..7c60c5c1c5a 100644 --- a/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/DiagnosticTestGenerated.java +++ b/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/DiagnosticTestGenerated.java @@ -770,6 +770,24 @@ public class DiagnosticTestGenerated extends AbstractDiagnosticTest { runTest("compiler/testData/diagnostics/tests/SyntaxErrorInTestHighlightingEof.kt"); } + @Test + @TestMetadata("tailRecBasic.kt") + public void testTailRecBasic() throws Exception { + runTest("compiler/testData/diagnostics/tests/tailRecBasic.kt"); + } + + @Test + @TestMetadata("tailRecInNestedScopes.kt") + public void testTailRecInNestedScopes() throws Exception { + runTest("compiler/testData/diagnostics/tests/tailRecInNestedScopes.kt"); + } + + @Test + @TestMetadata("tailRecInTry.kt") + public void testTailRecInTry() throws Exception { + runTest("compiler/testData/diagnostics/tests/tailRecInTry.kt"); + } + @Test @TestMetadata("tailRecOnVirtualMember.kt") public void testTailRecOnVirtualMember() throws Exception { @@ -788,6 +806,24 @@ public class DiagnosticTestGenerated extends AbstractDiagnosticTest { runTest("compiler/testData/diagnostics/tests/tailRecOverridden.kt"); } + @Test + @TestMetadata("tailRecSingleton.kt") + public void testTailRecSingleton() throws Exception { + runTest("compiler/testData/diagnostics/tests/tailRecSingleton.kt"); + } + + @Test + @TestMetadata("tailRecWithDispatchReceiver.kt") + public void testTailRecWithDispatchReceiver() throws Exception { + runTest("compiler/testData/diagnostics/tests/tailRecWithDispatchReceiver.kt"); + } + + @Test + @TestMetadata("tailRecWithExtensionReceiver.kt") + public void testTailRecWithExtensionReceiver() throws Exception { + runTest("compiler/testData/diagnostics/tests/tailRecWithExtensionReceiver.kt"); + } + @Test @TestMetadata("tailRecursionComplex.kt") public void testTailRecursionComplex() throws Exception { diff --git a/idea/idea-frontend-fir/idea-fir-low-level-api/tests/org/jetbrains/kotlin/idea/fir/low/level/api/diagnostic/compiler/based/DiagnosisCompilerTestFE10TestdataTestGenerated.java b/idea/idea-frontend-fir/idea-fir-low-level-api/tests/org/jetbrains/kotlin/idea/fir/low/level/api/diagnostic/compiler/based/DiagnosisCompilerTestFE10TestdataTestGenerated.java index 7c323d6de2c..1b61d2090da 100644 --- a/idea/idea-frontend-fir/idea-fir-low-level-api/tests/org/jetbrains/kotlin/idea/fir/low/level/api/diagnostic/compiler/based/DiagnosisCompilerTestFE10TestdataTestGenerated.java +++ b/idea/idea-frontend-fir/idea-fir-low-level-api/tests/org/jetbrains/kotlin/idea/fir/low/level/api/diagnostic/compiler/based/DiagnosisCompilerTestFE10TestdataTestGenerated.java @@ -770,6 +770,24 @@ public class DiagnosisCompilerTestFE10TestdataTestGenerated extends AbstractDiag runTest("compiler/testData/diagnostics/tests/SyntaxErrorInTestHighlightingEof.kt"); } + @Test + @TestMetadata("tailRecBasic.kt") + public void testTailRecBasic() throws Exception { + runTest("compiler/testData/diagnostics/tests/tailRecBasic.kt"); + } + + @Test + @TestMetadata("tailRecInNestedScopes.kt") + public void testTailRecInNestedScopes() throws Exception { + runTest("compiler/testData/diagnostics/tests/tailRecInNestedScopes.kt"); + } + + @Test + @TestMetadata("tailRecInTry.kt") + public void testTailRecInTry() throws Exception { + runTest("compiler/testData/diagnostics/tests/tailRecInTry.kt"); + } + @Test @TestMetadata("tailRecOnVirtualMember.kt") public void testTailRecOnVirtualMember() throws Exception { @@ -788,6 +806,24 @@ public class DiagnosisCompilerTestFE10TestdataTestGenerated extends AbstractDiag runTest("compiler/testData/diagnostics/tests/tailRecOverridden.kt"); } + @Test + @TestMetadata("tailRecSingleton.kt") + public void testTailRecSingleton() throws Exception { + runTest("compiler/testData/diagnostics/tests/tailRecSingleton.kt"); + } + + @Test + @TestMetadata("tailRecWithDispatchReceiver.kt") + public void testTailRecWithDispatchReceiver() throws Exception { + runTest("compiler/testData/diagnostics/tests/tailRecWithDispatchReceiver.kt"); + } + + @Test + @TestMetadata("tailRecWithExtensionReceiver.kt") + public void testTailRecWithExtensionReceiver() throws Exception { + runTest("compiler/testData/diagnostics/tests/tailRecWithExtensionReceiver.kt"); + } + @Test @TestMetadata("tailRecursionComplex.kt") public void testTailRecursionComplex() throws Exception { diff --git a/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/diagnostics/KtFirDataClassConverters.kt b/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/diagnostics/KtFirDataClassConverters.kt index 786e58f6bb8..1a5b3cedfdc 100644 --- a/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/diagnostics/KtFirDataClassConverters.kt +++ b/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/diagnostics/KtFirDataClassConverters.kt @@ -45,6 +45,7 @@ import org.jetbrains.kotlin.psi.KtImportDirective import org.jetbrains.kotlin.psi.KtModifierListOwner import org.jetbrains.kotlin.psi.KtNameReferenceExpression import org.jetbrains.kotlin.psi.KtNamedDeclaration +import org.jetbrains.kotlin.psi.KtNamedFunction import org.jetbrains.kotlin.psi.KtObjectDeclaration import org.jetbrains.kotlin.psi.KtParameter import org.jetbrains.kotlin.psi.KtPrimaryConstructor @@ -2169,6 +2170,30 @@ internal val KT_DIAGNOSTIC_CONVERTER = KtDiagnosticConverterBuilder.buildConvert token, ) } + add(FirErrors.NO_TAIL_CALLS_FOUND) { firDiagnostic -> + NoTailCallsFoundImpl( + firDiagnostic as FirPsiDiagnostic, + token, + ) + } + add(FirErrors.TAILREC_ON_VIRTUAL_MEMBER_ERROR) { firDiagnostic -> + TailrecOnVirtualMemberErrorImpl( + firDiagnostic as FirPsiDiagnostic, + token, + ) + } + add(FirErrors.NON_TAIL_RECURSIVE_CALL) { firDiagnostic -> + NonTailRecursiveCallImpl( + firDiagnostic as FirPsiDiagnostic, + token, + ) + } + add(FirErrors.TAIL_RECURSION_IN_TRY_IS_NOT_SUPPORTED) { firDiagnostic -> + TailRecursionInTryIsNotSupportedImpl( + firDiagnostic as FirPsiDiagnostic, + token, + ) + } add(FirErrors.FUN_INTERFACE_CONSTRUCTOR_REFERENCE) { firDiagnostic -> FunInterfaceConstructorReferenceImpl( firDiagnostic as FirPsiDiagnostic, diff --git a/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/diagnostics/KtFirDiagnostics.kt b/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/diagnostics/KtFirDiagnostics.kt index 1e2b9cf44f8..342e2dd7384 100644 --- a/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/diagnostics/KtFirDiagnostics.kt +++ b/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/diagnostics/KtFirDiagnostics.kt @@ -54,6 +54,7 @@ import org.jetbrains.kotlin.psi.KtImportDirective import org.jetbrains.kotlin.psi.KtModifierListOwner import org.jetbrains.kotlin.psi.KtNameReferenceExpression import org.jetbrains.kotlin.psi.KtNamedDeclaration +import org.jetbrains.kotlin.psi.KtNamedFunction import org.jetbrains.kotlin.psi.KtObjectDeclaration import org.jetbrains.kotlin.psi.KtParameter import org.jetbrains.kotlin.psi.KtPrimaryConstructor @@ -1542,6 +1543,22 @@ sealed class KtFirDiagnostic : KtDiagnosticWithPsi { override val diagnosticClass get() = CannotInferParameterType::class } + abstract class NoTailCallsFound : KtFirDiagnostic() { + override val diagnosticClass get() = NoTailCallsFound::class + } + + abstract class TailrecOnVirtualMemberError : KtFirDiagnostic() { + override val diagnosticClass get() = TailrecOnVirtualMemberError::class + } + + abstract class NonTailRecursiveCall : KtFirDiagnostic() { + override val diagnosticClass get() = NonTailRecursiveCall::class + } + + abstract class TailRecursionInTryIsNotSupported : KtFirDiagnostic() { + override val diagnosticClass get() = TailRecursionInTryIsNotSupported::class + } + abstract class FunInterfaceConstructorReference : KtFirDiagnostic() { override val diagnosticClass get() = FunInterfaceConstructorReference::class } diff --git a/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/diagnostics/KtFirDiagnosticsImpl.kt b/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/diagnostics/KtFirDiagnosticsImpl.kt index 55c0cb6f8cf..1bc058a3a79 100644 --- a/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/diagnostics/KtFirDiagnosticsImpl.kt +++ b/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/diagnostics/KtFirDiagnosticsImpl.kt @@ -56,6 +56,7 @@ import org.jetbrains.kotlin.psi.KtImportDirective import org.jetbrains.kotlin.psi.KtModifierListOwner import org.jetbrains.kotlin.psi.KtNameReferenceExpression import org.jetbrains.kotlin.psi.KtNamedDeclaration +import org.jetbrains.kotlin.psi.KtNamedFunction import org.jetbrains.kotlin.psi.KtObjectDeclaration import org.jetbrains.kotlin.psi.KtParameter import org.jetbrains.kotlin.psi.KtPrimaryConstructor @@ -2464,6 +2465,34 @@ internal class CannotInferParameterTypeImpl( override val firDiagnostic: FirPsiDiagnostic by weakRef(firDiagnostic) } +internal class NoTailCallsFoundImpl( + firDiagnostic: FirPsiDiagnostic, + override val token: ValidityToken, +) : KtFirDiagnostic.NoTailCallsFound(), KtAbstractFirDiagnostic { + override val firDiagnostic: FirPsiDiagnostic by weakRef(firDiagnostic) +} + +internal class TailrecOnVirtualMemberErrorImpl( + firDiagnostic: FirPsiDiagnostic, + override val token: ValidityToken, +) : KtFirDiagnostic.TailrecOnVirtualMemberError(), KtAbstractFirDiagnostic { + override val firDiagnostic: FirPsiDiagnostic by weakRef(firDiagnostic) +} + +internal class NonTailRecursiveCallImpl( + firDiagnostic: FirPsiDiagnostic, + override val token: ValidityToken, +) : KtFirDiagnostic.NonTailRecursiveCall(), KtAbstractFirDiagnostic { + override val firDiagnostic: FirPsiDiagnostic by weakRef(firDiagnostic) +} + +internal class TailRecursionInTryIsNotSupportedImpl( + firDiagnostic: FirPsiDiagnostic, + override val token: ValidityToken, +) : KtFirDiagnostic.TailRecursionInTryIsNotSupported(), KtAbstractFirDiagnostic { + override val firDiagnostic: FirPsiDiagnostic by weakRef(firDiagnostic) +} + internal class FunInterfaceConstructorReferenceImpl( firDiagnostic: FirPsiDiagnostic, override val token: ValidityToken,