微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

Scala 3 宏方法覆盖

如何解决Scala 3 宏方法覆盖

我正在尝试使用 Scala 3 宏和 TASTY 来覆盖一个方法。我想覆盖任何类型的任何方法。现在我从这个简单的案例开始。

我有一个测试基类:

class TestClass {
  def func(s: String) = "base"
}

我想实现这一点,但是使用 TASTY,因为我发现不可能在带有引号和拼接的泛型类型上调用 new A

'{
    new TestClass() {
       override def func(s: String) = "override"
    }
}.asExprOf[A]

我打印了上述代码的 AST,我几乎设法重新创建了它。问题是我无法将 New 称为生成的类 - 我看不到访问新类的符号或类型的方法。我还尝试使用新名称 Symbol.requiredClass(),虽然它返回了一些符号,但我在宏扩展过程中遇到了一个错误,即找不到该类。

我的问题是:

  • 是否可以派生自己的类型(除了在引号中使用显式 new Class {})?
  • ClassDef.copy 是否注册一个可以访问以创建新实例的新名称
  • 是否可以使用手动创建的 ClassDef 来创建类的实例?
  • 如何使用 Symbol.requiredClass 返回的符号,因为它会返回一些内容,即使之前没有定义?

我创建的代码

import scala.quoted.*

object NewClass {

  def newClassImpl[A: Type](e: Expr[A])(using Quotes): Expr[A] = {
    import quotes.reflect.*

    val typeRep = TypeRepr.of[A]

    val ret = typeRep.classSymbol.map(_.tree) match {
      case Some(
            cd @ ClassDef(
              name: String,constr: DefDef,parents: List[Tree],selfOpt: Option[ValDef],body: List[Statement]
            )
          ) =>
        println(cd.show(using Printer.TreeAnsiCode))

        val newItemsOwner = Symbol.spliceOwner.owner
        println("newItemsOwner = " + newItemsOwner)

        def createFunction(args: Term)(using Quotes): Term = {
          args
        }

        val newConstrSymbol = Symbol.newMethod(
          newItemsOwner,"<init>",MethodType(Nil)(
            _ => Nil,_ => TypeRepr.of[Unit]
          ),Flags.EmptyFlags,Symbol.noSymbol
        )

        val newConstrDef: DefDef = DefDef(
          newConstrSymbol,{
            case List(List(paramTerm: Term)) =>
              Some(createFunction(paramTerm).changeOwner(newConstrSymbol))
            case _ => None
          }
        )

        val newMethodSymbol = Symbol.newMethod(
          newItemsOwner,"func",MethodType(List("s"))(
            _ => List(TypeRepr.of[String]),_ => TypeRepr.of[String]
          ),Flags.Override,Symbol.noSymbol
        )

        val newMethodDef: DefDef = DefDef(
          newMethodSymbol,{
            case List(List(paramTerm: Term)) =>
              Some(createFunction(paramTerm).changeOwner(newMethodSymbol))
            case _ => None
          }
        )

        val parentSel = Select.unique(New(TypeTree.of[A]),"<init>")
        val parent = Apply(parentSel,Nil)

        val newClassDef: ClassDef = ClassDef.copy(cd)(
          name + "$gen",newConstrDef,parent :: Nil,None,newMethodDef :: Nil
        )

        val app = Apply(
          Select(New(TypeIdent(Symbol.requiredClass(name + "$gen"))),newConstrDef.symbol),Nil
        )
      
        val block = Block(newClassDef :: Nil,Typed(app,TypeTree.of[A]))
        val finalTerm = Inlined(Some(TypeTree.of[NewClass$]),Nil,block)

        println(finalTerm.show(using Printer.TreeAnsiCode))
        println(finalTerm.show(using Printer.TreeStructure))

        finalTerm.asExprOf[A]


      case other =>
        println("No class def found: " + other)
        e
    }

    println("Returned:")
    println(ret.asTerm.show(using Printer.TreeAnsiCode))
    println(ret.asTerm.show(using Printer.TreeStructure))

    ret
  }

  inline def newClass[A](a: A): A = ${ newClassImpl[A]('{ a }) }
}

返回的代码打印出来没有任何抱怨:

{
@scala.annotation.internal.sourceFile("src/main/scala/MethodsMain.scala") class TestClass$gen() extends TestClass {
    override def func(s: java.lang.String): java.lang.String = s
  }

  (new TestClass$gen(): TestClass)
}

但是如果由宏返回,我在扩展过程中会出错:

[error]   |Bad symbolic reference. A signature
[error]   |refers to TestClass$gen/T in package <empty> which is not available.
[error]   |It may be completely missing from the current classpath,or the version on
[error]   |the classpath might be incompatible with the version used when compiling the signature.
[error]   | This location contains code that was inlined from NewClass.scala:86

用法

val res:TestClass = NewClass.newClass[TestClass](new TestClass)

感谢您的帮助。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。