Is it possible to obtain the corresponding TypeTree from TypeParamRef using macro functionality? #17374
-
SummaryIs it possible to obtain the corresponding When given the following val trees: List[TypeTree] = ???
val typeParamRef: TypeRepr = ???
def findTypeTreeFromTypeParamRef =
trees.find(t => ???) Meta-problemI am considering mocking the identity method as follows: def identity[T](x: T): T
// expand to
def identity[T](x: T): T = mock$identity.apply(x).asInstanceOf[T]
val mock$identity = new MockFunction1[Any, Any] // function to store mock data To mock it, I need to implement DefDef and rhsFun as follows: def rhsFun(params: List[List[Tree]]) = ???
DefDef(cls, rhsFun) When generating Is there a good way to obtain the It appears that Here is a provisional implementation for mocking the identity method: // Macro.scala
import scala.quoted.*
import scala.annotation.experimental
@experimental
object Macro {
inline def m[T] = ${ impl[T] }
def impl[T](using Quotes, Type[T]): Expr[T] = {
import quotes.reflect.*
def returnType(symbol: Symbol): TypeRepr = symbol.info match {
case PolyType(_, _, MethodType(_, _, typeRepr)) =>
typeRepr
}
def makeDeclarations(cls: Symbol): List[Symbol] = {
TypeRepr.of[T].typeSymbol.declaredMethods.map { d =>
Symbol.newMethod(cls, d.name, d.info)
}
}
def makeMethods(cls: Symbol): List[Statement] = {
TypeRepr.of[T].typeSymbol.declaredMethods.map { d =>
def rhsFn(params: List[List[Tree]]): Option[Term] = {
val symbol = cls.declarations.find(_.name == d.name).head // not considering overloads, but it is assumed to not matter in this case
val returnType: TypeTree = {
val typeParamRef = symbol.info match
case PolyType(_, _, MethodType(_, _, r)) => r
params.flatten.collect {
// I tried to obtain the corresponding TypeTree from params using the return value TypeParamRef(T) of the identity method, but this implementation isn't working.
case t: TypeTree if t == TypeTree.of(using typeParamRef.asType) => t
}.head // java.util.NoSuchElementException: head of empty list
}
Some(
TypeApply(
Select(
'{ null }.asTerm, // provisional implementation
cls.methodMember("asInstanceOf").head
),
List(returnType) // find type parameter T from params
)
)
}
DefDef(cls.declarations.find(_.name == d.name).head, rhsFn)
}
}
val parents = List(TypeTree.of[Object], TypeTree.of[T])
val cls = Symbol.newClass(
Symbol.spliceOwner,
"Impl",
parents = parents.map(_.tpe),
makeDeclarations,
selfType = None
)
val clsDef = ClassDef(cls, parents, body = makeMethods(cls))
val newCls = Typed(
Apply(Select(New(TypeIdent(cls)), cls.primaryConstructor), Nil),
TypeTree.of[T]
)
println(Block(List(clsDef), newCls).asExprOf[T].show)
Block(List(clsDef), newCls).asExprOf[T]
}
}
// Main.scala
import scala.annotation.experimental
trait TestTrait {
def identity[T](x: T): T
}
@experimental
@main
def main(): Unit = {
Macro.m[TestTrait]
} |
Beta Was this translation helpful? Give feedback.
Replies: 1 comment
-
I may have found a way to solve the Meta-Problem, although it may not be a good approach: def makeMethods(cls: Symbol): List[Statement] = {
TypeRepr.of[T].typeSymbol.declaredMethods.map { d =>
def rhsFn(params: List[List[Tree]]): Option[Term] = {
val symbol = cls.declarations.find(_.name == d.name).head
val returnTypeInDecration = d.tree match
case DefDef(_, _, typeTree, _) =>
typeTree
val returnTypeInParams = params.flatten.collect {
case t: TypeTree if t.tpe.typeSymbol.name == returnTypeInDecration.tpe.typeSymbol.name =>
t
}
Some(
TypeApply(
Select(
'{ null }.asTerm,
cls.methodMember("asInstanceOf").head
),
returnTypeInParams
)
)
}
DefDef(cls.declarations.find(_.name == d.name).head, rhsFn)
}
} |
Beta Was this translation helpful? Give feedback.
I may have found a way to solve the Meta-Problem, although it may not be a good approach: