E199: Tailrec Nested Call

This warning is emitted when a @tailrec method contains a recursive call inside a non-inlined inner definition.

Tail recursion optimization can only be applied directly in the method's body. Recursive calls made through inner defs cannot be validated as tail recursive, nor can they be optimized. This means such calls could lead to stack overflow for deeply recursive calls.


Longer explanation:

Tail recursion is only validated and optimized directly in the definition. Any calls to the recursive method via an inner def cannot be validated as tail recursive, nor optimized if they are. To enable tail recursion from inner calls, mark the inner def as inline.


Example

import scala.annotation.tailrec

@tailrec
def countdown(n: Int): Unit =
  def helper(): Unit =
    countdown(n - 1)  // recursive call from inner def
  if n > 0 then helper()
  else countdown(n - 1)

Error

-- [E199] Syntax Warning: example.scala:6:13 -----------------------------------
6 |    countdown(n - 1)  // recursive call from inner def
  |    ^^^^^^^^^^^^^^^^
  |The tail recursive def countdown contains a recursive call inside the non-inlined inner def helper
  |-----------------------------------------------------------------------------
  | Explanation (enabled by `-explain`)
  |- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  | Tail recursion is only validated and optimised directly in the definition.
  | Any calls to the recursive method via an inner def cannot be validated as
  | tail recursive, nor optimised if they are. To enable tail recursion from
  | inner calls, mark the inner def as inline.
   -----------------------------------------------------------------------------

Solution

Mark the inner def as inline:

import scala.annotation.tailrec

@tailrec
def countdown(n: Int): Unit =
  inline def helper(): Unit =
    countdown(n - 1)
  if n > 0 then helper()
  else countdown(n - 1)

Or restructure the code to avoid nested recursive calls:

import scala.annotation.tailrec

@tailrec
def countdown(n: Int): Unit =
  if n > 0 then countdown(n - 1)