Solved: Scala 3 quotes: how to solve this StackOverflowError?

I implemented a recursive power version using macro quotes as a simpler example to show an issue I have. Here is the macro’s code:

  transparent inline def power2(a: Int, b:Int): Any = ${ power2Impl('a,'b) }

  def power2Impl(a: Expr[Int], b: Expr[Int])(using q1: Quotes): Expr[Int] =
    b match 
      case '{0} => '{1}
      case '{ $expr: tpe } => 
        println(s"b : ${b.show} = ${b.value}")
        println(s"b : ${Type.show[tpe]} = ${b.value}")
        '{
          val nb = $b-1
          val next = ${ power2Impl(a, 'nb)}
          $a * next
        }

and I invoke it so:

    val p2 = data.Macros3.power2(2,2)

and get this compile-time error:

    error]     |Exception occurred while executing macro expansion.
    [error]     |java.lang.StackOverflowError
    [error]     |	at scala.quoted.runtime.impl.Scope.root(SpliceScope.scala:23)
    [error]     |	at scala.quoted.runtime.impl.Scope.root$(SpliceScope.scala:13)
    [error]     |	at scala.quoted.runtime.impl.SpliceScope.root(SpliceScope.scala:36)
    [error]     |	at scala.quoted.runtime.impl.Scope.root(SpliceScope.scala:23)
    [error]     |	at scala.quoted.runtime.impl.Scope.root$(SpliceScope.scala:13)
    [error]     |	at scala.quoted.runtime.impl.SpliceScope.root(SpliceScope.scala:36)
    [error]     |	at scala.quoted.runtime.impl.Scope.root(SpliceScope.scala:23)
    [error]     |	at scala.quoted.runtime.impl.Scope.root$(SpliceScope.scala:13)
    [error]     |	at scala.quoted.runtime.impl.SpliceScope.root(SpliceScope.scala:36)
    [error]     |	at scala.quoted.runtime.impl.Scope.root(SpliceScope.scala:23)

So why is it not stopping when it should? The type checker (via IDE) reports that nb and next are both Int. So I added the print lines to see what is happening and get:

    b : 2 = Some(2)
    b : 2 = Some(2)
    b : nb = None
    b : nb.type = None
    b : nb = None
    b : nb.type = None
    b : nb = None
    b : nb.type = None
    b : nb = None

The lines are duplicated just to check the type. Note that at the start I have Some(2), but then after the 1st subtraction, the compiler seems to generate a None. I am expecting a Some(1). I assume the Some(0) would match the first case.

Can anyone tell me what is happening and how to solve this?

TIA

Alternate working version:

  def power2Impl(a: Expr[Int], b: Expr[Int])(using q1: Quotes): Expr[Int] =
    import quotes.reflect.*
    b match 
      case '{0} => '{1}
      case '{1} => a
      case '{ $expr: Int } => 
        println(s"b(a) : ${b.show} = ${b.value}")
        val next_b: Expr[Int] = '{$expr - 1}
        next_b.value match
          case None => 
            report.errorAndAbort("power2Impl: Unexpected value")
          case Some(0) => 
            // a^1 = a * a⁰
            a
          case Some(1) => 
            // a^2 = a * a¹
            '{$a*$a}
          case Some(x) => 
            // a^b = a * a^x
            println(s"Recursing on Some($x)")
            val next = power2Impl(a, next_b)
            println(s"next = ${next.value}")
            '{$a * $next}

The problem is that in the previous version, the line:

val nb = $b-1

is code that will be generated, and the value is therefore not available when we splice :

${ power2Impl(a, 'nb)}

hence the value None.

The problem is explained in detail by @nicolasstucki. He was kind enough to provide a best-practice example. Much appreciated. Here is the link: