Панель инструментов не может оценить значения времени выполнения.Это написано в скалярном документе: https://github.com/scala/scala/blob/2.13.x/src/reflect/scala/reflect/macros/Evals.scala#L61-L67
Давайте изменим ваш макрос
def impl(c: blackbox.Context)(x: c.Expr[String]): c.Expr[String] = {
import c.universe._
println(s"input: ${showRaw(x.tree)}") // added
val x1 = c.Expr[String](c.untypecheck(x.tree.duplicate))
println(s"compile-time value is: ${c.eval(x1)}")
x
}
Тогда у нас будет
object App {
/*class*/ object Y {
val foo = "GOOF"
val boo = Z.test(Z.foo)//Warning:scalac: input: Select(Select(Ident(Macros), Macros.Z), TermName("foo"))
//Warning:scalac: compile-time value is: WOOF
// val boo1 = Z.test(Y.foo)//Warning:scalac: input: Select(Select(This(TypeName("App")), App.Y), TermName("foo"))
//Error: exception during macro expansion:
// java.lang.ClassNotFoundException: App$Y$
// val boo2 = Z.test((new Y).foo)//Warning:scalac: input: Select(Apply(Select(New(Select(This(TypeName("App")), App.Y)), termNames.CONSTRUCTOR), List()), TermName("foo"))
//Error: exception during macro expansion:
// java.lang.ClassNotFoundException: App$Y
// val boo3 = Z.test(foo) //Warning:scalac: input: Select(This(TypeName("Y")), TermName("foo"))
//Error: exception during macro expansion:
// scala.tools.reflect.ToolBoxError: reflective compilation has failed:
// Internal error: unable to find the outer accessor symbol of object __wrapper$1$fd3cb1297ce8421e809ee5e821c2f708
// or
//Error: exception during macro expansion:
// java.lang.ClassNotFoundException: App$Y$
val boo4 = Z.test("abc")//Warning:scalac: input: Literal(Constant("abc"))
//Warning:scalac: compile-time value is: abc
val boo5 = Z.test("abc" + "DEF")//Warning:scalac: input: Literal(Constant("abcDEF"))
//Warning:scalac: compile-time value is: abcDEF
}
}
Дерево This
означает, что оно представляетзначение времени выполнения.Просто ClassNotFoundException
иногда происходит быстрее, чем ToolBoxError
.Ваш подпроект с макросами project 1
не зависит от подпроекта project 2
, поэтому во время компиляции макросов Y
не найден.
Разница между Z.foo
и foo
(он же Y.foo
) равенчто foo
на самом деле this.foo
(компилятору здесь все равно, является ли Y
классом или объектом) и его можно переопределить в подклассах.
Quill не использует eval
.Он разбирает дерево на собственное AST , если может, или оставляет Dynamic
, если не может (т.е. если дерево соответствует значению времени выполнения).И затем он работает с этими двумя случаями по-разному: либо во время раскрытия макросов с QueryMeta
, либо во время компиляции + во время выполнения с Decoder
https://github.com/getquill/quill/blob/master/quill-core/src/main/scala/io/getquill/context/QueryMacro.scala#L34-L38
Таким образом, обходной путь составляет работать со значениями времени выполнения во время выполнения.
def impl(c: blackbox.Context)(x: c.Expr[String]): c.Expr[String] = {
import c.universe._
println(s"input: ${showRaw(x.tree)}")
try {
val x1 = c.Expr[String](c.untypecheck(x.tree.duplicate))
val x2 = c.eval(x1)
println(s"compile-time value is: $x2")
c.Expr[String](q"$x2")
} catch {
case ex: Throwable =>
println(ex.getMessage)
x
}
}
Это похоже на https://github.com/getquill/quill/blob/master/quill-core/src/main/scala/io/getquill/context/ContextMacro.scala#L66-L68