繁体   English   中英

如何使用在一个 Scala 宏执行中创建和类型检查的 `universe.Tree`,在另一个宏执行中?

[英]How to use a `universe.Tree` created and type-checked in one Scala macro execution, in another macro execution?

在 JSON 库jsfacile 中,我能够自动为具有递归类型引用的类型派生类型类,而不是到缓冲区,其中宏的外部执行存储universe.Tree实例,稍后由相同或其他宏的内部执行使用。 只要未对universe.Tree实例进行类型检查(使用Typers.typecheck方法),此方法就可以正常工作。

问题是这会强制对同一个Tree实例进行多次类型检查:一次是在创建它的宏执行中(在将它存储在缓冲区中之后); 并且在每个需要它的内部宏执行中执行更多次。

这个问题的目的是找出一种在类型检查后在宏执行之间共享Tree实例的方法; 以提高编译速度。

我试图将类型检查的Tree包装到一个universe.Expr[Unit]并通过Expr.in[U <: Universe](otherMirror: Mirror[U]): U # Expr[T]方法。 但它因内部错误而失败:

Internal error: unable to find the outer accessor symbol of class <Name of the class where the macro is expanded>

任何的想法?

通常,手动对树进行类型检查并在不同上下文之间共享类型树是一个坏主意。 请参阅以下示例:

import scala.language.experimental.macros
import scala.reflect.macros.whitebox
import scala.collection.mutable

object Macros {
  val mtcCache = mutable.Map[whitebox.Context#Type, whitebox.Context#Tree]()

  trait MyTypeclass[A]

  object MyTypeclass {
    implicit def materialize[A]: MyTypeclass[A] = macro materializeImpl[A]

    def materializeImpl[A: c.WeakTypeTag](c: whitebox.Context): c.Tree = {
      import c.universe._

      val typA = weakTypeOf[A]

      if (!mtcCache.contains(typA)) 
        mtcCache += (typA -> c.typecheck(q"new MyTypeclass[$typA] {}"))

      mtcCache(typA).asInstanceOf[Tree]
    }
  }
}

import Macros.MyTypeclass

object App { // Internal error: unable to find the outer accessor symbol of object App

  class A { // Internal error: unable to find the outer accessor symbol of class A

    class B { // Internal error: unable to find the outer accessor symbol of class A

      class C {
        implicitly[MyTypeclass[Int]] // new MyTypeclass[Int] {} is created and typechecked here
      }

      implicitly[MyTypeclass[Int]] // cached typed instance is inserted here, this is the reason of above error
    }

    implicitly[MyTypeclass[Int]] // cached typed instance is inserted here, this is the reason of above error
  }

  implicitly[MyTypeclass[Int]] // cached typed instance is inserted here, this is the reason of above error
}

斯卡拉 2.13.3。

implicitly我们在某些地方放置了符号所有者链不正确的树。

如果你创建ABC对象,那么错误就会消失(所以这是否会阻止编译取决于运气)。

此外,如果您删除c.typecheck那么错误就会消失。

此外,如果我们返回c.untypecheck(mtcCache(typA).asInstanceOf[Tree])而不是mtcCache(typA).asInstanceOf[Tree]则错误消失。 但有时c.typecheck + c.untypecheck会损坏树。

因此,如果您需要两者,则可以尝试将树的无类型和有类型版本都放入缓存中,但返回无类型版本

type CTree = whitebox.Context#Tree
val mtcCache = mutable.Map[whitebox.Context#Type, (CTree, CTree)]()

trait MyTypeclass[A]

object MyTypeclass {
  implicit def materialize[A]: MyTypeclass[A] = macro materializeImpl[A]

  def materializeImpl[A: c.WeakTypeTag](c: whitebox.Context): c.Tree = {
    import c.universe._

    val typA = weakTypeOf[A]
    val tree = q"new MyTypeclass[$typA] {}"
    if (!mtcCache.contains(typA)) 
      mtcCache += (typA -> (tree, c.typecheck(tree)))

    mtcCache(typA)._1.asInstanceOf[Tree]
  }
}

或者如果您只需要类型检查来触发递归,那么您可以对一棵树进行类型检查,将无类型树放入缓存并返回无类型树

val mtcCache = mutable.Map[whitebox.Context#Type, whitebox.Context#Tree]()

trait MyTypeclass[A]

object MyTypeclass {
  implicit def materialize[A]: MyTypeclass[A] = macro materializeImpl[A]

  def materializeImpl[A: c.WeakTypeTag](c: whitebox.Context): c.Tree = {
    import c.universe._

    val typA = weakTypeOf[A]
    val tree = q"new MyTypeclass[$typA] {}"
    c.typecheck(tree)
    if (!mtcCache.contains(typA)) mtcCache += (typA -> tree)

    mtcCache(typA).asInstanceOf[Tree]
  }
}

拉取请求: https : //github.com/readren/json-facile/pull/1

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM