-
Notifications
You must be signed in to change notification settings - Fork 1.1k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
@tailrec and nested definitions #20105
Comments
I had a play with nesting re-writes. Would be keen to here thoughts on automatically inlining calls to sub-methods in the tail position in cases where it calls the parent method. def a(i: Int): Unit =
def b(i: Int): Unit =
???
a(i)
if (???)
b(i) // not inlined
???
else
???
b(i) // inlined In the alternative, is anybody clear as to if the following should compile or not - I think defining this one would inform all the others: @tailrec
def foo(i: Int): Unit =
def bar(j: Int) =
???
foo(j)
???
if (???)
foo(i + 1)
else
bar(i) In this case, there are direct calls from |
I don't think we want to automate inlining for the purpose of allowing tail calls. At best we could augment the |
Am I correct in reading into the suggestion we augment the error message in this case, that the last example I posted should not compile? If we agree weather or not my last sample above should compile despite the non tail indirect call - Ill have a bash at that at some point. |
Yes the last example should indeed compile. (When evaluating |
I'm generally aware of the level of recursion, I was just a tad confused as to how the concept of self would refer to arbitrary children. Ill rephrase the issue description to more accurately point at the real issue, then have a bit of a poke when I get free time. |
The Scala 2 ticket I take as asking for either rewrite of local methods or error (but not silence) is scala/bug#8767 I assumed it would just error, but I'll take a lead from whatever solution is devised here. |
Either erroring or ignoring (potentially with a warn) are fairly simple. A rewrite is not always possible, for example if b is defined, then passed into a pre compiled method, rather than being called directly. The example in the scala 2 ticket is very different - its two separate methods that call each other - that example looks like either implementing non self tail recursion, or solving the halting problem, neither of which I belive we wish to attempt here. Our example here is where the second method is a sub method of the first, and we currently have an example which clearly incorrectly compiles (as it has no recursive calls at all). Main focus is on preventing that, but this does open up the question about nested methods. |
Above we disscussed the following: @tailrec
def foo(i: Int): Unit =
def bar(j: Int) =
???
foo(j)
???
if (???)
foo(i + 1)
else
bar(i) The immediate feedback was that this should compile, due to the difference between self-tail recursion and full tail recursion. def bar(x: => Int) = 1
@tailrec
def foo(i: Int): Unit =
if (???)
foo(i + 1)
else
bar{
???
foo(i + 1)
???
} In this case the recursive call is in implicit rather than explicit inner method - but this is obviously a very subtle difference from a technical perspective. |
Updated the PR - but I am not sure on having such a difference between explicit and implicit inner methods - it just feels unclean to me. |
…ons contain non-tail recursive calls. Code will now compile where a child def calls the parent def in a non-tail position (with the warning). Code will no longer compile if all calls to a @tailrec method are in named child methods (as these do not tail recurse).
Compiler version
3.4.0
(also validated on near latest local build
3.5.0-RC1-bin-SNAPSHOT-nonbootstrapped-git-9a5b9b4
Minimized code
This compiles:
This does not:
Output
These should no compile, as there are no tail recursive calls within the outer
@tailrec
annotated block. It has erroneously detected that this is ok, due to the rewrite of the innerbar
method. This should not compile, and if this were not annotated with@tailrec
, the outertailLabel
should not be generated.Looking into the one that does compile, it generates an unutilised label
testLabel2[Unit]:
, and fails to warn about having no taill recursion optimised calls on the to level method:
Expectation
For the behaviour between these two (weather this is an acceptable place to recur or not) to be consistent between these cases - or in the alternative - to support multi tiered tail recursion.
Notes
This compiles (and indeed rewrites to a loop) if the inner method is declared as inline. Potentially a rule that within annotated
@tailrec
methods, any inner methods that call the top level method are handled asinline
for simple uses directly within this method?I was having a play around, and while complex, it does look feasible that we could have some sort of rewrite that processes this as a single larger rewrite. What would people thing of optimiseing to something like this:
The text was updated successfully, but these errors were encountered: