我需要在给定的位置寻找一个隐含的值。我在类中保留了以前宏调用中的位置,如下所示:
class Delayed[+Kind[_[_]]](val sourceFilePath: String, val callSitePoint: Int) {
def find[F[_]]: Kind[F] = macro Impl.find[Kind, F]
}前面的宏非常简单:
def build[Kind[_[_]]](c: blackbox.Context): c.Expr[Delayed[Kind]] = {
import c.universe._
c.Expr(
q"""
new Delayed(${c.enclosingPosition.point}, ${c.enclosingPosition.source.path})
"""
)
}有了这个位置,我所要做的就是启动隐式搜索,对吗?
def find[Kind[_[_]], F[_]](c: blackbox.Context)(implicit kindTag: c.WeakTypeTag[Kind[F]], fTag: c.WeakTypeTag[F[_]]): c.Expr[Kind[F]] = {
import c.universe._
reify {
val self = c.prefix.splice.asInstanceOf[Delayed[Kind]]
val sourceFile = AbstractFile.getFile(self.sourceFilePath)
val batchSourceFile = new BatchSourceFile(sourceFile, sourceFile.toCharArray)
val implicitSearchPosition = new OffsetPosition(batchSourceFile, self.callSitePoint).asInstanceOf[c.Position]
c.Expr[Kind[F]](c.inferImplicitValue(
appliedType(kindTag.tpe.typeConstructor, fTag.tpe.typeConstructor),
pos = implicitSearchPosition
)).splice
}
}我使用reify/splice调用获得职位,然后应用inferImplicitValue。但是编译器抱怨隐式值上的最后一个剪接:
the splice cannot be resolved statically,
which means there is a cross-stage evaluation involved它要求我将编译器jar添加为依赖项,但这样做只会得到另一个错误:
Macro expansion contains free term variable c defined by find in Delayed.scala我明白,在概念上,具体化是在价值的世界里。我不明白的是,在将宏生成的代码写入我的源代码之前,应该解析隐式搜索。这是我唯一能想到的让隐式搜索在宏上下文中工作的方法。
我哪里错了?我确实理解编译器的消息,但对我来说,在这个特定的上下文中,这是没有意义的。也许我不明白inferImplicitValue是如何工作的。
发布于 2019-04-23 09:46:26
试试Context#eval(expr)
def find[Kind[_[_]], F[_]](c: blackbox.Context)(implicit kindTag: c.WeakTypeTag[Kind[F]], fTag: c.WeakTypeTag[F[_]]): c.Expr[Kind[F]] = {
import c.universe._
val self = c.eval(c.Expr[Delayed[Kind]](c.untypecheck(c.prefix.tree.duplicate)))
val sourceFile = AbstractFile.getFile(self.sourceFilePath)
val batchSourceFile = new BatchSourceFile(sourceFile, sourceFile.toCharArray)
val implicitSearchPosition = new OffsetPosition(batchSourceFile, self.callSitePoint).asInstanceOf[c.Position]
c.Expr[Kind[F]](c.inferImplicitValue(
appliedType(kindTag.tpe.typeConstructor, fTag.tpe.typeConstructor),
pos = implicitSearchPosition
))
}或者,在计算前缀定义之前,您可以尝试找到前缀定义的右侧:
macros__/src/main/scala/Delayed.scala
import scala.language.experimental.macros
import scala.reflect.internal.util.{BatchSourceFile, OffsetPosition}
import scala.reflect.io.AbstractFile
import scala.reflect.macros.whitebox
class Delayed[+Kind[_[_]]](val sourceFilePath: String, val callSitePoint: Int) {
def find[F[_]]: Kind[F] = macro Impl.find[Kind, F]
}
object Delayed {
def build[Kind[_[_]]]: Delayed[Kind] = macro Impl.build[Kind]
}
class Impl(val c: whitebox.Context) {
import c.universe._
def build[Kind[_[_]]](implicit kindTag: c.WeakTypeTag[Kind[Any]/*[F] forSome {type F[_]}*/]): c.Expr[Delayed[Kind]] = {
c.Expr[Delayed[Kind]](
q"""
new Delayed[${kindTag.tpe.typeConstructor}](${c.enclosingPosition.source.path}, ${c.enclosingPosition.point})
"""
)
}
def find[Kind[_[_]], F[_]](implicit kindTag: c.WeakTypeTag[Kind[Any]], fTag: c.WeakTypeTag[F[_]]): c.Expr[Kind[F]] = {
val prefix = c.prefix.tree
val prefixSymbol = prefix.symbol
def eval[A: WeakTypeTag](tree: Tree): Either[Throwable, A] = {
// import org.scalamacros.resetallattrs._ // libraryDependencies += "org.scalamacros" %% "resetallattrs" % "1.0.0" // https://github.com/scalamacros/resetallattrs
// util.Try(c.eval(c.Expr[A](c.resetAllAttrs(tree.duplicate)))).toEither
util.Try(c.eval(c.Expr[A](c.untypecheck(c.typecheck(tree/*.duplicate*/))))).toEither // see (*) below
}
val self: Delayed[Kind] = eval[Delayed[Kind]](prefix).orElse {
var rhs: Either[Throwable, Tree] = Left(new RuntimeException(s"can't find RHS of definition of $prefix"))
val traverser = new Traverser {
override def traverse(tree: Tree): Unit = {
tree match {
case q"$_ val $_: $_ = $expr"
if tree.symbol == prefixSymbol ||
(tree.symbol.isTerm && tree.symbol.asTerm.getter == prefixSymbol) =>
rhs = Right(expr)
case _ =>
super.traverse(tree)
}
}
}
c.enclosingRun.units.foreach(unit => traverser.traverse(unit.body))
rhs.flatMap(eval[Delayed[Kind]])
}.fold(err => c.abort(c.enclosingPosition, s"can't find or eval self because: $err"), identity)
val sourceFile = AbstractFile.getFile(self.sourceFilePath)
val batchSourceFile = new BatchSourceFile(sourceFile, sourceFile.toCharArray)
val implicitSearchPosition = new OffsetPosition(batchSourceFile, self.callSitePoint).asInstanceOf[c.Position]
c.Expr[Kind[F]](c.inferImplicitValue(
appliedType(kindTag.tpe.typeConstructor, fTag.tpe.typeConstructor),
silent = false,
pos = implicitSearchPosition
))
}
}但我怀疑具有指定位置的c.inferImplicitValue不像您预期的那样工作。我猜该位置用于错误消息,而不是用于管理隐式解析范围。如果您想以这种方式处理隐式解析的优先级,可以使用编译器内部(1 2 3. 4. 5)。
macros__/src/main/scala/Functor.scala
trait Functor[F[_]]core__/src/main/scala/App.scala
object A {
implicit val f: Functor[List] = new Functor[List] {}
val d: Delayed[Functor] = Delayed.build[Functor]
}
object App {
implicit val f1: Functor[List] = new Functor[List] {}
A.d.find[List] // scalac: App.this.f1 // not A.f
}斯卡拉2.13.10
当提供分配给val的lambda时,获取scala.MatchError: F(类scala.reflect.internal.Trees$Ident)
Scala宏如何使用参数默认值将MethodSymbol转换为DefDef?
Scala:在Context.eval引用中可以编写什么代码?
如何从Scala 3宏的外部范围获取变量初始化的主体? (Scala 3)
(*) .duplicate实际上是不必要的:https://github.com/readren/json-facile/pull/1#issuecomment-733886784
原则上,在此阶段计算的值可以通过序列化/反序列化(允许一种跨阶段评估)保留到下一阶段。
但是序列化c: Context是不可能的。
https://stackoverflow.com/questions/55807221
复制相似问题