FIR: permit tailrec calls in inline lambdas
This commit is contained in:
+8
-30
@@ -36,16 +36,8 @@ object FirTailrecFunctionChecker : FirSimpleFunctionChecker() {
|
|||||||
var catchScopeCount = 0
|
var catchScopeCount = 0
|
||||||
var finallyScopeCount = 0
|
var finallyScopeCount = 0
|
||||||
var tailrecCount = 0
|
var tailrecCount = 0
|
||||||
var lambdaScopeCount = 0
|
|
||||||
graph.traverse(TraverseDirection.Forward, object : ControlFlowGraphVisitorVoid() {
|
graph.traverse(TraverseDirection.Forward, object : ControlFlowGraphVisitorVoid() {
|
||||||
override fun visitNode(node: CFGNode<*>) {}
|
override fun visitNode(node: CFGNode<*>) {}
|
||||||
override fun visitPostponedLambdaEnterNode(node: PostponedLambdaEnterNode) {
|
|
||||||
lambdaScopeCount++
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun visitPostponedLambdaExitNode(node: PostponedLambdaExitNode) {
|
|
||||||
lambdaScopeCount--
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun visitTryMainBlockEnterNode(node: TryMainBlockEnterNode) {
|
override fun visitTryMainBlockEnterNode(node: TryMainBlockEnterNode) {
|
||||||
tryScopeCount++
|
tryScopeCount++
|
||||||
@@ -81,30 +73,16 @@ object FirTailrecFunctionChecker : FirSimpleFunctionChecker() {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
val dispatchReceiver = functionCall.dispatchReceiver
|
val dispatchReceiver = functionCall.dispatchReceiver
|
||||||
// A tailrec call does not support changing dispatchers. Here we report changing dispatch receiver if the dispatch receiver
|
val dispatchReceiverOwner = declaration.dispatchReceiverType?.toSymbol(context.session) as? FirClassSymbol<*>
|
||||||
// is present and not a `this` or a singleton. For the `this` check, we don't need to actually compare if the dispatch
|
val sameReceiver = dispatchReceiver is FirNoReceiverExpression ||
|
||||||
// receiver `this` references the same `this` made available from `declaration`. This is because
|
(dispatchReceiver is FirThisReceiverExpression && dispatchReceiver.calleeReference.boundSymbol == dispatchReceiverOwner) ||
|
||||||
// 1. if `this` is not labeled, then it references the innermost `this` receiver. If the innermost scope is not the
|
dispatchReceiverOwner?.classKind?.isSingleton == true
|
||||||
// `declaration` body, then follow-up checks on following nodes would report there to be more instructions, which would
|
if (!sameReceiver) {
|
||||||
// then make this call non-tailrec.
|
// A call on a different receiver might get dispatched to a different method, so it can't be optimized.
|
||||||
// 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.
|
|
||||||
if (dispatchReceiver !is FirThisReceiverExpression &&
|
|
||||||
dispatchReceiver !is FirNoReceiverExpression &&
|
|
||||||
(declaration.dispatchReceiverType?.toSymbol(context.session) as? FirClassSymbol<*>)?.classKind?.isSingleton != true
|
|
||||||
) {
|
|
||||||
reporter.reportOn(functionCall.source, FirErrors.NON_TAIL_RECURSIVE_CALL, context)
|
reporter.reportOn(functionCall.source, FirErrors.NON_TAIL_RECURSIVE_CALL, context)
|
||||||
return
|
} else if (tryScopeCount > 0 || catchScopeCount > 0 || finallyScopeCount > 0) {
|
||||||
}
|
|
||||||
if (tryScopeCount > 0 || catchScopeCount > 0 || finallyScopeCount > 0) {
|
|
||||||
reporter.reportOn(functionCall.source, FirErrors.TAIL_RECURSION_IN_TRY_IS_NOT_SUPPORTED, context)
|
reporter.reportOn(functionCall.source, FirErrors.TAIL_RECURSION_IN_TRY_IS_NOT_SUPPORTED, context)
|
||||||
} else if (lambdaScopeCount > 0 || node.hasMoreFollowingInstructions(declaration)) {
|
} else if (node.hasMoreFollowingInstructions(declaration)) {
|
||||||
reporter.reportOn(functionCall.source, FirErrors.NON_TAIL_RECURSIVE_CALL, context)
|
reporter.reportOn(functionCall.source, FirErrors.NON_TAIL_RECURSIVE_CALL, context)
|
||||||
} else if (!node.isDead) {
|
} else if (!node.isDead) {
|
||||||
tailrecCount++
|
tailrecCount++
|
||||||
|
|||||||
Vendored
-1
@@ -1,6 +1,5 @@
|
|||||||
// KT-16549
|
// KT-16549
|
||||||
// IGNORE_BACKEND: JVM, JS
|
// IGNORE_BACKEND: JVM, JS
|
||||||
// IGNORE_FIR_DIAGNOSTICS_DIFF
|
|
||||||
|
|
||||||
class TailInline {
|
class TailInline {
|
||||||
private inline fun act(action: () -> Unit) {
|
private inline fun act(action: () -> Unit) {
|
||||||
|
|||||||
-1
@@ -1,7 +1,6 @@
|
|||||||
// KT-14961
|
// KT-14961
|
||||||
// IGNORE_BACKEND: JVM, JS_IR, WASM
|
// IGNORE_BACKEND: JVM, JS_IR, WASM
|
||||||
// WITH_RUNTIME
|
// WITH_RUNTIME
|
||||||
// IGNORE_FIR_DIAGNOSTICS_DIFF
|
|
||||||
|
|
||||||
fun listOfFactor(number: Int): List<Int> {
|
fun listOfFactor(number: Int): List<Int> {
|
||||||
tailrec fun listOfFactor(number: Int, acc: List<Int>): List<Int> {
|
tailrec fun listOfFactor(number: Int, acc: List<Int>): List<Int> {
|
||||||
|
|||||||
@@ -1,37 +0,0 @@
|
|||||||
// 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<!>()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,3 +1,4 @@
|
|||||||
|
// FIR_IDENTICAL
|
||||||
// WITH_STDLIB
|
// WITH_STDLIB
|
||||||
|
|
||||||
<!NO_TAIL_CALLS_FOUND!>tailrec<!> fun foo1() {
|
<!NO_TAIL_CALLS_FOUND!>tailrec<!> fun foo1() {
|
||||||
|
|||||||
Reference in New Issue
Block a user