Skip to content

macros: remove or restrict symbol.tree to avoid CyclicReference exception #7025

Closed
@liufengyun

Description

@liufengyun

Current macro system exposes symbol.tree to get the tree of a symbol. However, its usage in macros easily leads to CyclicReference exception.

minimized code

Macro_1.scala:

  import scala.quoted._
  import scala.tasty._

  inline def debug: Unit = ${debugImpl}

  def debugImpl given (qctx: QuoteContext): Expr[Unit] = {
    import qctx.tasty._

    def nearestEnclosingDef(owner: Symbol): Symbol =
      owner match {
        case IsDefDefSymbol(x)   => x
        case IsClassDefSymbol(x) => x
        case _                   => nearestEnclosingDef(owner.owner)
      }

    nearestEnclosingDef(rootContext.owner) match {
        case IsDefDefSymbol(x) =>
          val code = x.tree.show
          '{ println(${code.toExpr}) }
        case _ =>
          '{()}
      }
  }
}

Test_2.scala:

object Test {
  def main(args: Array[String]): Unit = {
    bar("world", 100, true)
  }

  def bar(a1: String, a2: Long, a3: Boolean) = {  // works fine with explicit return type
    debug
  }
}

expectation

The code above leads to CyclicReference exception:

stack trace
-- Error: Test_2.scala:7:4 ---------------------------------------------------------------------
7 |    debug
  |    ^^^^^
  |Exception occurred while executing macro expansion.
  |dotty.tools.dotc.core.CyclicReference:
  |	at dotty.tools.dotc.core.CyclicReference$.apply(TypeErrors.scala:157)
  |	at dotty.tools.dotc.core.SymDenotations$SymDenotation.completeFrom(SymDenotations.scala:249)
  |	at dotty.tools.dotc.core.Denotations$Denotation.completeInfo$1(Denotations.scala:180)
  |	at dotty.tools.dotc.core.Denotations$Denotation.info(Denotations.scala:182)
  |	at dotty.tools.dotc.ast.tpd$.polyDefDef(tpd.scala:224)
  |	at dotty.tools.dotc.ast.tpd$.DefDef(tpd.scala:221)
  |	at dotty.tools.dotc.ast.tpd$.DefDef(tpd.scala:218)
  |	at dotty.tools.dotc.tastyreflect.FromSymbol$.defDefFromSym(FromSymbol.scala:46)
  |	at dotty.tools.dotc.tastyreflect.ReflectionCompilerInterface.DefDefSymbol_tree(ReflectionCompilerInterface.scala:1644)
  |	at dotty.tools.dotc.tastyreflect.ReflectionCompilerInterface.DefDefSymbol_tree(ReflectionCompilerInterface.scala:1643)
  |	at scala.tasty.reflect.SymbolOps$DefDefSymbolAPI.tree(SymbolOps.scala:217)
  |	at Macros$.debugImpl(Macro_1.scala:24)
  |
  | This location is in code that was inlined at Test_2.scala:7

To avoid such crashes with symbol.tree, we may either:

  • remove symbol.tree in the API (scala2 macros doesn't have such an API)
  • only expose the API in black-box macros, and make black-box macros expand in a later phase

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions