user5279007
user5279007

Reputation: 255

Is there any way to turn off tail recursion optimization of Scala Compiler?

For some special reasons, I want to remove the effect of all @tailrec in a big program, but don't want to do that manually, is there any optional arguments when compiling that can turn off the tail recursion optimization? I want to just leave the @tailrec in the code, but don't want to check it and do tail recursion optimization when compiling. Is that possible?

Upvotes: 2

Views: 698

Answers (2)

jwvh
jwvh

Reputation: 51271

The @tailrec annotation doesn't turn on/off tail recursion optimization. It only tells the compiler to error out if the annotated routine is not properly tail recursive.

If the routine is tail recursive then the optimization is there whether it's annotated or not.

From the docs:

A method annotation which verifies that the method will be compiled with tail call optimization.

If it is present, the compiler will issue an error if the method cannot be optimized into a loop.

The operative word being "verifies".

UPDATE

@mikołak is correct. A quick check shows that the -g:notailcalls flag turns off annotation verification.

%%> scalac tailrec.scala 
tailrec.scala:5: error: could not optimize @tailrec annotated method rf: it contains a recursive call not in tail position
def rf(x: Int): Int = if (x < 1) x else rf(x-2)+1
                                               ^
one error found
%%> scalac -g:notailcalls tailrec.scala 
%%>

Upvotes: 3

mikołak
mikołak

Reputation: 9705

You can use the compiler's -g:notailcalls option.

From the docs:

-g:{none,source,line,vars,notailcalls}

"none" generates no debugging info,

"source" generates only the source file attribute,

"line" generates source and line number information,

"vars" generates source, line number and local variable information,

"notailcalls" generates all of the above and will not perform tail call optimization.

Note, as the docs specify, that this will also enable debug info generation.


An example:

import scala.annotation.tailrec

class A {

@tailrec
final def x(i: Int): Int = if(i == 0) {i;} else {x(i-1)}

}

javap -p -v A.class of x when compiled "normally":

public final int x(int);
    descriptor: (I)I
    flags: ACC_PUBLIC, ACC_FINAL
    Code:
      stack=2, locals=2, args_size=2
         0: iload_1
         1: iconst_0
         2: if_icmpne     7
         5: iload_1
         6: ireturn
         7: iload_1
         8: iconst_1
         9: isub
        10: istore_1
        11: goto          0

The same, when compiled with -g:notailcalls:

public final int x(int);
    descriptor: (I)I
    flags: ACC_PUBLIC, ACC_FINAL
    Code:
      stack=3, locals=2, args_size=2
         0: iload_1
         1: iconst_0
         2: if_icmpne     9
         5: iload_1
         6: goto          16
         9: aload_0
        10: iload_1
        11: iconst_1
        12: isub
        13: invokevirtual #12                 // Method x:(I)I
        16: ireturn

The important bit is the invokevirtual on position 13.


Note, by the way, that jwvh prudently corrected you on the interpetation of @tailrec - it doesn't actually toggle tail optimization, the only thing that it does is that it informs the compiler to fail if it cannot insert a tail call optimization, which it would attempt anyway.

Upvotes: 6

Related Questions