import scala.annotation.tailrec
trait Iterator { def hasNext: Boolean }
object Iterable {
val i1: Iterator = ???
val iterator = new Iterator {
@tailrec def hasNext: Boolean =
if (!i1.hasNext) false
else {/* i1.next; */ hasNext}
}
}
[error] 10 | if (!i1.hasNext) false
[error] | ^^^^^^^^^^
[error] | Cannot rewrite recursive call: it targets a supertype
compile successfully as in scala 2
public final class Iterable$$anon.Iterable$$anon$1 implements Iterator {
public boolean hasNext() {
while($outer.Iterable$$i1.hasNext()) ;
return false;
}
private final Iterable $outer;
...
@tailrec, dotty also compile to while/for loop:private static final class Iterable$$anon$1 implements Iterator {
private final Iterable $outer;
public boolean hasNext() {
for (Iterator iterator = (Iterator)this; iterator.Iterable$_$$anon$$$outer().Iterable$$i1.hasNext(); iterator = iterator) {}
return false;
}
...
It means that dotty can get rid of all calls to the method. So it should NOT throw compile errors when we add @tailrec?
Both dotty and scalac do not rewrite the recursive call i1.hasNext. I think it's reasonable for @tailrec to tell you when this happen, and so I would just remove @tailrec from the method call.
I think only the second hasNext call (in else clause) is recursive.
Or not? 馃榿
To be precise, we don't know statically whether it's a recursive call to the same hasNext method or not, it depends on the runtime class of i1, so @tailrec conservatively disallows it.
Note that @tailrec does not require recursive calls to be on the same class instance, for example this is valid:
class Test {
@tailrec
final def foo(cond: Boolean, that: Test): Boolean =
if (cond)
that.foo(!cond, this)
else
cond
}
The call to that.foo is considered to be a recursive call to foo with a different receiver.
Closing as "working as intended"