FIR Checker: check tailrec

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