如何使用"宇宙".树`在一个Scala宏执行中创建并检查类型,在另一个宏执行中



在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
}

Scala 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

最新更新