MacroAnnotation

scala.annotation.MacroAnnotation

Base trait for macro annotation that will transform a definition

Attributes

Experimental
true
Graph
Supertypes
class Annotation
class Object
trait Matchable
class Any

Members list

Value members

Abstract methods

Transform the tree definition and add other definitions

Transform the tree definition and add other definitions

This method takes as argument the annotated definition. It returns a non-empty list containing the modified version of the annotated definition. The new tree for the definition must use the original symbol. New definitions can be added to the list before or after the transformed definitions, this order will be retained.

All definitions in the result must have the same owner. The owner can be recovered from tree.symbol.owner.

The result cannot contain class, object or type definition. This limitation will be relaxed in the future.

When developing and testing a macro annotation, you must enable -Xcheck-macros and -Ycheck:all.

Example:

import scala.quoted.*
import scala.collection.mutable

class memoize extends MacroAnnotation:
  def transform(using Quotes)(tree: quotes.reflect.Definition): List[quotes.reflect.Definition] =
    import quotes.reflect._
    tree match
      case DefDef(name, TermParamClause(param :: Nil) :: Nil, tpt, Some(rhsTree)) =>
        (Ref(param.symbol).asExpr, rhsTree.asExpr) match
          case ('{ $paramRefExpr: t }, '{ $rhsExpr: u }) =>
            val cacheSymbol = Symbol.newUniqueVal(tree.symbol.owner, name + "Cache", TypeRepr.of[mutable.Map[t, u]], Flags.Private, Symbol.noSymbol)
            val cacheRhs = '{ mutable.Map.empty[t, u] }.asTerm
            val cacheVal = ValDef(cacheSymbol, Some(cacheRhs))
            val cacheRefExpr = Ref(cacheSymbol).asExprOf[mutable.Map[t, u]]
            val newRhs = '{ $cacheRefExpr.getOrElseUpdate($paramRefExpr, $rhsExpr) }.asTerm
            val newTree = DefDef.copy(tree)(name, TermParamClause(param :: Nil) :: Nil, tpt, Some(newRhs))
            List(cacheVal, newTree)
      case _ =>
        report.error("Annotation only supported on `def` with a single argument are supported")
        List(tree)

with this macro annotation a user can write

@memoize
def fib(n: Int): Int =
  println(s"compute fib of $n")
  if n <= 1 then n else fib(n - 1) + fib(n - 2)

and the macro will modify the definition to create

val fibCache =
   scala.collection.mutable.Map.empty[Int, Int]
 def fib(n: Int): Int =
   fibCache.getOrElseUpdate(
     n,
     {
       println(s"compute fib of $n")
       if n <= 1 then n else fib(n - 1) + fib(n - 2)
     }
   )

Value parameters

Quotes

Implicit instance of Quotes used for tree reflection

tree

Tree that will be transformed

Attributes