重写并实现特征的注释宏,未正确处理泛型

2024-05-08

我正在编写一个宏,需要创建一个重写特征的类,该类具有相同的特征方法/参数,但返回类型不同。

所以说我们有:

trait MyTrait[T]
{
  def x(t1: T)(t2: T): T
}

@AnnProxy
class MyClass[T] extends MyTrait[T]

MyClass 将被重写为:

class MyClass[T] {
 def x(t1: T)(t2: T): R[T]
}

(所以 x 现在将返回 R[T] 而不是 T)

我编写了宏并对其进行了调试,它生成了以下代码:

Expr[Any](class MyClass[T] extends scala.AnyRef {
   def <init>() = {
     super.<init>();
     ()
   };
   def x(t1: T)(t2: T): macrotests.R[T] = $qmark$qmark$qmark
 })
@AnnProxy

正如你所看到的,签名似乎没问题。但是当尝试使用宏时,我收到编译错误:

    val my = new MyClass[Int]
    my.x(5)(6)

错误:(14, 7) 类型不匹配; 发现:整数(5) 必需:T xx(5)(6) ^

所以看来该方法的泛型 T 与类 [T] 不同。有什么想法如何解决吗?

这是我到目前为止的宏。我对宏不太擅长(在 stackoverflow 的很多帮助下创造了这个),但这是当前状态:

@compileTimeOnly("enable macro paradise to expand macro annotations")
class AnnProxy extends StaticAnnotation
{
    def macroTransform(annottees: Any*): Any = macro IdentityMacro.impl
}

trait R[T]

object IdentityMacro
{

private val SDKClasses = Set("java.lang.Object", "scala.Any")

def impl(c: whitebox.Context)(annottees: c.Expr[Any]*): c.Expr[Any] = {
    import c.universe._

    def showInfo(s: String) = c.info(c.enclosingPosition, s.split("\n").mkString("\n |---macro info---\n |", "\n |", ""), true)

    val classDef = annottees.map(_.tree).head.asInstanceOf[ClassDef]
    val clazz = c.typecheck(classDef).symbol.asClass
    val tparams = clazz.typeParams
    val baseClasses = clazz.baseClasses.tail.filter(clz => !SDKClasses(clz.fullName))
    val methods = baseClasses.flatMap {
        base =>
            base.info.decls.filter(d => d.isMethod && d.isPublic).map { decl =>
                val termName = decl.name.toTermName
                val method = decl.asMethod
                val params = method.paramLists.map(_.map {
                    s =>
                        val vd = internal.valDef(s)

                        val f = tparams.find(_.name == vd.tpt.symbol.name)
                        val sym = if (f.nonEmpty) f.get else vd.tpt.symbol

                        q"val ${vd.name} : $sym "
                })
                val paramVars = method.paramLists.flatMap(_.map(_.name))

                q""" def $termName (...$params)(timeout:scala.concurrent.duration.FiniteDuration) : macrotests.R[${method.returnType}] = {
            ???
           }"""
            }
    }

    val cde = c.Expr[Any] {
        q"""
      class ${classDef.name} [..${classDef.tparams}] {
        ..$methods
      }
  """
    }
    showInfo(show(cde))
    cde
}
}

编辑:我设法通过将类构建为字符串然后使用 c.parse 来编译它来解决问题。感觉就像黑客,但它确实有效。一定有更好的方法来操纵树。

package macrotests

import scala.annotation.{StaticAnnotation, compileTimeOnly}
import scala.language.experimental.macros
import scala.reflect.macros.whitebox

@compileTimeOnly("enable macro paradise to expand macro annotations")
class AnnProxy extends StaticAnnotation
{
    def macroTransform(annottees: Any*): Any = macro AnnProxyMacro.impl
}

trait R[T]

trait Remote[T]

object AnnProxyMacro
{

private val SDKClasses = Set("java.lang.Object", "scala.Any")

def impl(c: whitebox.Context)(annottees: c.Expr[Any]*): c.Expr[Any] = {
    import c.universe._

    val classDef = annottees.map(_.tree).head.asInstanceOf[ClassDef]
    val clazz = c.typecheck(classDef).symbol.asClass

    val baseClasses = clazz.baseClasses.tail.filter(clz => !SDKClasses(clz.fullName))
    val methods = baseClasses.flatMap {
        base =>
            base.info.decls.filter(d => d.isMethod && d.isPublic).map { decl =>
                val termName = decl.name.toTermName
                val method = decl.asMethod
                val params = method.paramLists.map(_.map {
                    s =>
                        val vd = internal.valDef(s)
                        val tq = vd.tpt
                        s"${vd.name} : $tq"
                })
                val paramVars = method.paramLists.flatMap(_.map(_.name))
                val paramVarsArray = paramVars.mkString("Array(", ",", ")")


                val paramsStr = params.map(_.mkString("(", ",", ")")).mkString(" ")
                val retTpe = method.returnType.typeArgs.mkString("-unexpected-")
                s""" def $termName $paramsStr (timeout:scala.concurrent.duration.FiniteDuration) : macrotests.Remote[$retTpe] = {
      println($paramVarsArray.toList)
            new macrotests.Remote[$retTpe] {}
           }"""
            }
    }

    val tparams = clazz.typeParams.map(_.name)
    val tparamsStr = if (tparams.isEmpty) "" else tparams.mkString("[", ",", "]")
    val code =
        s"""
           |class ${classDef.name}$tparamsStr (x:Int) {
           |${methods.mkString("\n")}
           |}
         """.stripMargin
    //      print(code)
    val cde = c.Expr[Any](c.parse(code))
    cde
}
}

代码很长,可以看github:https://github.com/1178615156/scala-macro-example/blob/master/stackoverflow/src/main/scala/so/AnnotationWithTrait.scala https://github.com/1178615156/scala-macro-example/blob/master/stackoverflow/src/main/scala/so/AnnotationWithTrait.scala

import scala.annotation.StaticAnnotation
import scala.language.experimental.macros
import scala.reflect.macros.blackbox.Context

/**
  * Created by yu jie shui on 2015/12/2.
  */

class AnnotationWithTrait extends StaticAnnotation {
  def macroTransform(annottees: Any*): Any = macro AnnotationWithTraitImpl.apply

}

class AnnotationWithTraitImpl(val c: Context) {

  import c.universe._

  val SDKClasses = Set("java.lang.Object", "scala.Any")

  def showInfo(s: String) = c.info(c.enclosingPosition, s.split("\n").mkString("\n |---macro info---\n |", "\n |", ""), true)

  def apply(annottees: c.Expr[Any]*) = {

    val classDef = annottees.map(_.tree).head.asInstanceOf[ClassDef]

    val superClassSymbol= c.typecheck(classDef).symbol.asClass.baseClasses.tail
      .filterNot(e => SDKClasses.contains(e.fullName)).reverse

    val superClassTree= classDef match {
      case q"$mod class $name[..$t](..$params) extends ..$superClass { ..$body }" =>
        (superClass: List[Tree]).filterNot(e =>
          typeOf[Object].members.exists(_.name == e.children.head.toString())
        )
    }

    showInfo(show(superClassSymbol))
    showInfo(show(superClassTree))

    val impl = q"private[this] object ${TermName("impl")} extends ..${superClassTree}"
    //

    //get super class all can call method
    val methods = superClassSymbol.map(_.info.members
      .filterNot(_.isConstructor)
      .filterNot(e => typeOf[Object].members.exists(_.name == e.name)).map(_.asMethod)).toList

    case class ReplaceTypeParams(from: String, to: String)
    type ClassReplace = List[ReplaceTypeParams]

    //trait a[A]
    //class b[B] extends a[B]
    //need replace type params A to B
    val classReplaceList: List[ClassReplace] = superClassTree zip superClassSymbol map {
      case (superClassTree, superClassSymbol) =>
        superClassSymbol.asClass.typeParams.map(_.name) zip superClassTree.children.tail map
          (e => ReplaceTypeParams(e._1.toString, e._2.toString()))
    }

    val out = classReplaceList zip methods map {
      case (classReplace, func) =>

        func map { e => {

          val funcName = e.name

          val funcTypeParams = e.typeParams.map(_.name.toString).map(name => {
            TypeDef(Modifiers(Flag.PARAM), TypeName(name), List(), TypeBoundsTree(EmptyTree, EmptyTree))
          })

          val funcParams = e.paramLists.map(_.map(e => q"${e.name.toTermName}:${
            TypeName(
              classReplace.find(_.from == e.info.toString).map(_.to).getOrElse(e.info.toString)
            )} "))

          val funcResultType = TypeName(
            classReplace.find(_.from == e.returnType.toString).map(_.to).getOrElse(e.info.toString)
          )
          q"""
           def ${funcName}[..${funcTypeParams}](...$funcParams):${funcResultType}=
              impl.${funcName}[..${funcTypeParams}](...$funcParams)
            """
        }
        }

    }

    showInfo(show(out))

    q"""
       class ${classDef.name}[..${classDef.tparams}]{
        $impl
        ..${out.flatten}
       }
      """
  }
}

test

trait MyTrait[MT1] {

  def x(t1: MT1)(t2: MT1): MT1 = t1

}

trait MyTrait2[MT2] {
  def t(t2: MT2): MT2 = t2
}


@AnnotationWithTrait
class MyClass[MCT1, MCT2] extends MyTrait[MCT1] with MyTrait2[MCT2]

object AnnotationWithTraitUsing extends App {
  assert(new MyClass[Int, String].x(1)(2) == 1)
  assert(new MyClass[Int, String].t("aaa") == "aaa")
}
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

重写并实现特征的注释宏,未正确处理泛型 的相关文章

  • 使用Scala宏生成方法

    我想在 Scala 2 11 中使用注释宏生成方法的别名 我什至不确定这是否可能 如果是 怎么办 示例 鉴于下面的内容 我希望注释宏扩展到 class Socket alias aliases Seq ask read def load n
  • 是否有一个类型类可以检查至少一个隐式类型的存在?

    我有一个特质Foo T U 和一个类型级算法 给出L lt HList和目标类型U 告诉我是否存在T in L这样就存在一个隐含的Foo T U 在适用范围 这是使用以下类型类实现的 trait Search L lt HList U ob
  • 是否可以从 scala 宏内的 WeakTypeTag 生成 Apply ?

    我有一个WeakTypeTag我的宏中的某种类型 我想生成如下代码 macroCreate SomeObject gt SomeObject 1 宏的定义将是这样的 def macroCreate A macro macroCreate A
  • 使用方法为案例类生成伴随对象(字段 = 方法)

    使用 scala macros 为案例类生成伴随对象 我尝试过的一些代码示例 它有效 我可以获取元组列表 名称 gt 类型 但如何在同一范围内生成对象 import c universe val tpe weakTypeOf T val f
  • Scala 宏:定义顶级对象

    我看了type macros对于斯卡拉 但是当我想从示例创建对象时 我收到错误 Example scala 7 gt or lt expected type Test url String macro impl Example scala
  • Scala 宏:检查某个注释

    感谢以下问题的解答我之前的问题 https stackoverflow com q 17223213 397695 我能够创建一个函数宏 使其返回一个Map将每个字段名称映射到其类的值 例如 trait Model case class U
  • 在多阶段编译中,我们是否应该使用标准的序列化方法来通过阶段传送对象?

    这个问题是在 Scala 3 Dotty 中提出的 但应该推广到 MetaML 系列之外的任何语言 Scala 3 宏教程 https docs scala lang org scala3 reference metaprogramming
  • Scala 宏:使用准引号访问成员

    我正在尝试实现一个隐式物化器 如下所述 http docs scala lang org overviews macros implicits html http docs scala lang org overviews macros i
  • Scala 宏检查树的匿名函数

    我刚刚开始使用宏 感觉我错过了一些非常明显的痛苦 我想检查 AST 中是否有传递给我的宏的匿名 lambda 函数 最终我想对它做一些事情 但我在第一个障碍上就失败了 我的代码如下所示 object Test extends App doI
  • Scala 宏:从 Scala 中类的字段中创建映射

    假设我有很多类似的数据类 这是一个示例类User其定义如下 case class User name String age Int posts List String val numPosts Int posts length def fo
  • 如何将宏注释应用于具有上下文绑定的案例类?

    当我尝试向案例类添加宏注释时 macid case class CC A T val x A 我收到错误 private this not allowed for case class parameters macid只是恒等函数 定义为白
  • POM/JAR中出现的宏依赖

    我有一个使用宏的 scala 项目 它基本上遵循此处描述的确切方法 http www scala sbt org 0 12 4 docs Detailed Topics Macro Projects html http www scala
  • Scala / Dotty - 将特征混合到现有对象中

    有没有办法将特征混合到 Dotty 或 Scala 中的现有对象中 class SomeClass trait SomeTrait This works but it s not what I m looking for new SomeC
  • 当宏注释不能在定义它的同一编译中使用时,这意味着什么?

    我对这个说法很好奇 错误 3 18 另一种可能性是您尝试使用宏 定义它的同一编译运行中的注释 我尝试谷歌搜索并发现了这个 最后 请记住 使用宏需要分两步进行编译 首先编译宏 然后编译使用宏的代码 这是必要的 以便您的宏可以在编译其余代码之前
  • 使用 Scala 3 宏重写方法

    我正在尝试使用 Scala 3 宏和 TASTY 重写方法 我想重写任何类型的任何方法 现在我从这个简单的案例开始 我有一个测试基类 class TestClass def func s String base 我想实现这一点 但使用 TA
  • 在 sbt 中添加仅编译时的子项目依赖项

    我有一个多项目包含一个私有宏子项目 其用途仅限于实现其他子项目的方法体 它既不应该出现在其他子项目的运行时类路径上 也不应该以任何形式在其他子项目已发布的 POM 中可见 这样其他 sbt 项目就可以在不知道宏子项目的情况下使用该项目中的库
  • 是否可以定义带有可变参数的宏,并获取每个参数的类型?

    下面是一个明显的可变参数函数 def fun xs Any 我们可以用类似的方式定义一个宏 def funImpl c Context xs c Expr Any fun 1 1 1 0 但在本例中 所有参数都键入为Any 事实上 编译器在
  • Scala:在运行时替换方法

    假设我有课 class Original def originalMethod 1 现在 假设我有一个例子 val instance new Original 现在是否可以做点什么instance在运行时替换originalMethod用不
  • 如何用其他树替换子树?

    在 Scala 宏中 我想做这样的事情 我有一个Tree 可能很大 现在我想找到这棵树的一个具有某种具体形式的子树 例如Apply 现在我想创建一棵新树 它是原始树的副本 但找到的子树被其他树替换 例如 通过类似的方法 我可以用调用某些其他
  • 重写并实现特征的注释宏,未正确处理泛型

    我正在编写一个宏 需要创建一个重写特征的类 该类具有相同的特征方法 参数 但返回类型不同 所以说我们有 trait MyTrait T def x t1 T t2 T T AnnProxy class MyClass T extends M

随机推荐