Open smarter opened 5 years ago
I think it's open to interpretation whether Scala 2 or 3 is right here.
For instance, this example compiles in both 2 and 3:
import scala.annotation.tailrec
trait HiHo {
final def ho(cond: Boolean): Boolean = hi(cond)
@tailrec
final def hi(cond: Boolean): Boolean =
ho(cond) && hi(cond)
}
Essentially it's the same thing: there are 2 branches, one of which can be tailrec optimized and one which cannot.
And actually this example can be proven to eventually stackoverflow while self.hi(cond)
could be perfectly safe.
I don't think @tailrec
can handle mutual recursion
Indeed it can't, but basically my point is that scala 2 accepts @tailrec
as long as there is a self-recursive call that can be optimized. Whether or not there are other method calls in there doesn't matter.
Scala 3 does the same thing except when one of those other method calls looks like a recursive call but targets a supertype.
In both cases the compiler manages to optimize an actual tail recursive call. And in both cases that other method call is not guaranteed to be either safe or unsafe.
Ahh I see what you mean, that makes sense 👍
Not sure what the context is (that motivated commenting), but this is one of a few tailrec tickets I'm addressing (today or so). There is also a ticket on mutual recursion of local methods. Hopefully after coffee and sunrise, the comments will also make sense to me.
We don't really know if the super method is recursive or not.
The preference on the dotty ticket for x => f()
was to warn conservatively for any possibility of recursivity.
The following outputs an error as expected:
The following also outputs an error:
But this compiles without error: