Cyclic delegates

The compiler doesn’t seem to check for cyclic delegates, e.g. if you do something silly like this …

interface A
interface B

class AImpl : A, B by BImpl()
class BImpl : B, A by AImpl()

fun main() {
    AImpl()
}

… it compiles just fine. Of course, when you run it, the result is very predictably…

Exception in thread "main" java.lang.StackOverflowError
at rummage.BImpl.<init>(delegateCallback.kt:7)
at rummage.AImpl.<init>(delegateCallback.kt:6)
at rummage.BImpl.<init>(delegateCallback.kt:7)
at rummage.AImpl.<init>(delegateCallback.kt:6)
...

I mean, in this case it’s just well deserved punishment, but maybe there are cases where it’s not as obvious (e.g. getting the delegate dependency via the superclass or a constructor arg or so), so it might be still a good idea for the compiler to check?

(tried this with Kotlin 1.9.20 on JVM, btw)

1 Like

I doubt the compiler even tries to understand the execution flow between functions as such analysis is pretty complicated and in most cases not very precise. It doesn’t complain even here:

fun foo() {
    foo()
}
2 Likes

If the delegate comes from an argument how can the compiler check anything? It depends on what a caller passes in.

That’s technically recursion though, right? Recursion is a valid use-case as long as you have a terminating condition. I would have thought that IntelliJ picks that up as an infinite recursive function and warns you.

1 Like