Try 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
))
}
Alternatively you can try to find the right hand side of the prefix definition before evaluating it:
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
))
}
}
But I suspect that c.inferImplicitValue
with specified position works not as you expected. I guess the position is used for error messages, not for managing scopes of implicit resolution. If you want to play with priority of implicit resolution in such way you could use compiler internals (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
}
Scala 2.13.10
Def Macro, pass parameter from a value
Get an scala.MatchError: f (of class scala.reflect.internal.Trees$Ident) when providing a lambda assigned to a val
Creating a method definition tree from a method symbol and a body
Scala macro how to convert a MethodSymbol to DefDef with parameter default values?
How to get the runtime value of parameter passed to a Scala macro?
Scala: what can code in Context.eval reference?
How to get the body of variable initialisation from outer scope in Scala 3 macros? (Scala 3)
(*) .duplicate
is actually not necessary: https://github.com/readren/json-facile/pull/1#issuecomment-733886784
In principle, a value calculated at this stage can be persisted for the next stage via serialization/deserialization (permitting a kind of cross-stage evaluation)
How to use quasiquotes with previously defined object
But it's hardly possible to serialize c: Context
.