Scala 3 宏特征参数

用户3747260

这是基本设置:

trait MyProduct[A, B](a: A, b:  B)
class X
class Y

class XxY(x: X, y: Y) extends MyProduct(x,y)

我正在尝试检查特征的论据MyProduct更具体地说,我想提取一些信息,例如字符串“x”和“y”,它们指示XxY传递给MyProduct.

重要的是,我需要满足具有相同类型的多个字段的情况,例如class XxYxY(x: X, y1: Y, y2: Y) extends MyProduct3(x, y1, y2),从类型参数进行推理是不够的。

我想也许问题是我还没有找到一种方法来获得extends条款本身的符号我可以找到ClassDeffor XxY,从中我可以提取parents并获取已构造的 type MyProduct[X,Y]我还查看symbol.declarations了封闭模块和XxY.<init>函数的 ,以查看是否有可用于查找参数的数据,但这些似乎都没有我正在寻找的信息。

查看封闭模块的树让我觉得也许这些信息被删除了,而是需要从源代码中解析为文本,但我希望有人有更好的解决方案。

从评论编辑:

作为输入,我有一个Type封闭对象/模块实例。例如:

inline def simpleProductTest[T <: Singleton]: String = ${simpleProductTestImpl[T]}

def simpleProductTestImpl[T](using q: Quotes)(using Type[T]): Expr[String] = ???

作为输出,我想要一些允许我检查 extends 子句中使用的特征的参数的东西。

米特罗·米廷

如果你这样做

import scala.quoted.*

object Macros {
  inline def simpleProductTest[T]: Unit = ${simpleProductTestImpl[T]}

  def simpleProductTestImpl[T](using Quotes)(using Type[T]): Expr[Unit] = {
    import quotes.reflect.*
    println(TypeRepr.of[T].typeSymbol.tree.asInstanceOf[TypeDef].rhs)
    '{()}
  }
}

你会看到的

object App {
  trait MyProduct[A, B](a: A, b:  B)
  class X
  class Y

  class XxY(x: X, y: Y, z: Int) extends MyProduct(x,y)

  Macros.simpleProductTest[XxY]
}

没有关于 x,y

Template(
  DefDef(
    <init>,
    List(List(
      ValDef(x,TypeTree[TypeRef(ThisType(TypeRef(ThisType(TypeRef(NoPrefix,module class <empty>)),module class App$)),class X)],EmptyTree),
      ValDef(y,TypeTree[TypeRef(ThisType(TypeRef(ThisType(TypeRef(NoPrefix,module class <empty>)),module class App$)),class Y)],EmptyTree),
      ValDef(z,TypeTree[TypeRef(TermRef(ThisType(TypeRef(NoPrefix,module class <root>)),object scala),class Int)],EmptyTree)
    )),
    TypeTree[TypeRef(ThisType(TypeRef(ThisType(TypeRef(NoPrefix,module class <empty>)),module class App$)),class XxY)],
    EmptyTree
  ),
  List(
    TypeTree[TypeRef(ThisType(TypeRef(NoPrefix,module class lang)),class Object)],
    TypeTree[AppliedType(TypeRef(ThisType(TypeRef(ThisType(TypeRef(NoPrefix,module class <empty>)),module class App$)),trait MyProduct),
      List(
        TypeRef(ThisType(TypeRef(ThisType(TypeRef(NoPrefix,module class <empty>)),module class App$)),class X),
        TypeRef(ThisType(TypeRef(ThisType(TypeRef(NoPrefix,module class <empty>)),module class App$)),class Y)
      )
    )]
  ),
  ValDef(_,EmptyTree,EmptyTree),
  List(
    ValDef(x,TypeTree[TypeRef(ThisType(TypeRef(ThisType(TypeRef(NoPrefix,module class <empty>)),module class App$)),class X)],EmptyTree),
    ValDef(y,TypeTree[TypeRef(ThisType(TypeRef(ThisType(TypeRef(NoPrefix,module class <empty>)),module class App$)),class Y)],EmptyTree),
    ValDef(z,TypeTree[TypeRef(TermRef(ThisType(TypeRef(NoPrefix,module class <root>)),object scala),class Int)],EmptyTree)
  )
)

并且Template在 中不存在scala.quoted.*,它是dotty.tools.dotc.ast.Trees.Template

但是如果你.tastyTASTy 检查打印文件

libraryDependencies += "org.scala-lang" %% "scala3-tasty-inspector" % "3.0.0"

import scala.quoted.*
import scala.tasty.inspector.*

class MyInspector extends Inspector:
  def inspect(using Quotes)(tastys: List[Tasty[quotes.type]]): Unit =
    import quotes.reflect.*
    for tasty <- tastys do
      val tree = tasty.ast
      println(tree)

@main def test: Unit =
  val tastyFiles = List("target/scala-3.0.0/classes/App.tasty")
  TastyInspector.inspectTastyFiles(tastyFiles)(new MyInspector)

你会看到的

Template(
  DefDef(
    <init>,
    List(List(
      ValDef(x,Ident(X),EmptyTree),
      ValDef(y,Ident(Y),EmptyTree),
      ValDef(z,Ident(Int),EmptyTree)
    )),
    TypeTree[TypeRef(TermRef(ThisType(TypeRef(NoPrefix,module class <root>)),object scala),Unit)],
    EmptyTree
  ),
  List(
    Apply(Select(New(TypeTree[TypeRef(TermRef(ThisType(TypeRef(NoPrefix,module class java)),object lang),Object)]),<init>),List()),
    Apply(
      TypeApply(
        Select(New(Ident(MyProduct)),<init>),
        List(
          TypeTree[TypeRef(ThisType(TypeRef(ThisType(TypeRef(ThisType(TypeRef(NoPrefix,module class <root>)),module class <empty>)),module class App$)),class X)],
          TypeTree[TypeRef(ThisType(TypeRef(ThisType(TypeRef(ThisType(TypeRef(NoPrefix,module class <root>)),module class <empty>)),module class App$)),class Y)]
        )
      ),
      List(Ident(x), Ident(y))  <--- HERE!!!
    )
  ),
  ValDef(_,EmptyTree,EmptyTree),
  List(
    ValDef(x,TypeTree[TypeRef(ThisType(TypeRef(ThisType(TypeRef(ThisType(TypeRef(NoPrefix,module class <root>)),module class <empty>)),module class App$)),class X)],EmptyTree),
    ValDef(y,TypeTree[TypeRef(ThisType(TypeRef(ThisType(TypeRef(ThisType(TypeRef(NoPrefix,module class <root>)),module class <empty>)),module class App$)),class Y)],EmptyTree),
    ValDef(z,TypeTree[TypeRef(TermRef(ThisType(TypeRef(NoPrefix,module class <root>)),object scala),Int)],EmptyTree)
  )
)

这棵树略有不同,有关的信息就x,y在那里。

在Scaladocs的扩展方法.treeSymbol它的书面

     *  **Warning**: avoid using this method in macros.
     *
     *  **Caveat**: The tree is not guaranteed to exist unless the compiler
     *  option `-Yretain-trees` is enabled.

让我们开启 -Yretain-trees

scalacOptions += "-Yretain-trees"

然后Macros.simpleProductTest[XxY]打印

Template(
  DefDef(
    <init>,
    List(List(
      ValDef(x,Ident(X),EmptyTree),
      ValDef(y,Ident(Y),EmptyTree),
      ValDef(z,Ident(Int),EmptyTree)
    )),
    TypeTree[TypeRef(ThisType(TypeRef(NoPrefix,module class scala)),class Unit)],
    EmptyTree
  ),
  List(
    Apply(Select(New(TypeTree[TypeRef(ThisType(TypeRef(NoPrefix,module class lang)),class Object)]),<init>),List()),
    Apply(
      TypeApply(
        Select(New(Ident(MyProduct)),<init>),
        List(
          TypeTree[TypeVar(TypeParamRef(A) -> TypeRef(ThisType(TypeRef(ThisType(TypeRef(NoPrefix,module class <empty>)),module class App$)),class X))],
          TypeTree[TypeVar(TypeParamRef(B) -> TypeRef(ThisType(TypeRef(ThisType(TypeRef(NoPrefix,module class <empty>)),module class App$)),class Y))]
        )
      ),
      List(Ident(x), Ident(y))   <----  AGAIN HERE !!!!
    )
  ),
  ValDef(_,EmptyTree,EmptyTree),
  List(
    ValDef(x,TypeTree[TypeRef(ThisType(TypeRef(ThisType(TypeRef(NoPrefix,module class <empty>)),module class App$)),class X)],EmptyTree),
    ValDef(y,TypeTree[TypeRef(ThisType(TypeRef(ThisType(TypeRef(NoPrefix,module class <empty>)),module class App$)),class Y)],EmptyTree),
    ValDef(z,TypeTree[TypeRef(TermRef(ThisType(TypeRef(NoPrefix,module class <root>)),object scala),class Int)],EmptyTree)
  )
)

即树再次略有不同,但有关x,y出现的信息所以现在的问题只是如何提取这些信息,因为它是dotty.tools.dotc.ast.*

case class Template[-T >: Untyped] private[ast](
  constr: DefDef[T], 
  parentsOrDerived: List[Tree[T]], 
  self: ValDef[T], 
  private var preBody: LazyTreeList[T @uncheckedVariance]
)(implicit @constructorOnly src: SourceFile)

所以最后

libraryDependencies += "org.scala-lang" %% "scala3-compiler" % "3.0.0"

import scala.quoted.*

inline def simpleProductTest[T]: List[String] = ${simpleProductTestImpl[T]}

def simpleProductTestImpl[T](using Quotes)(using Type[T]): Expr[List[String]] = {
  import quotes.reflect.*
  Expr(
    TypeRepr.of[T].typeSymbol.tree.asInstanceOf[TypeDef].rhs
      .asInstanceOf[dotty.tools.dotc.ast.Trees.Template[?]]
      .parentsOrDerived
      .asInstanceOf[List[Apply]]
      .tail.head.args.map(_.symbol.name)
  )
}

Macros.simpleProductTest[XxY] // List(x, y)

本文收集自互联网,转载请注明来源。

如有侵权,请联系 [email protected] 删除。

编辑于
0

我来说两句

0 条评论
登录 后参与评论

相关文章