scalascala-macrosscala-3

Scala 3 macro: how to get the return type of a method by its `Symbol`?


In a Scala 3 macro, given a Symbol for a method, what would be the most straightforward way to get the return type of that method?

More concretely, suppose I have a type name Foo that corresponds to a trait, and a method name. I can obtain a Symbol for that name with this:

// elsewhere
trait Foo:
  def bar[A](a: A)(b: String): Boolean

// in the macro
val methodSymbol = TypeRepr.of[Foo].typeSymbol.methodMember("bar").head

I can use methodSymbol.info to get the full signature of methodSymbol, but that can be an arbitrarily nested chain of MethodTypes and PolyTypes. How do I get the final return type without traversing this chain? In the example, how do I get a TypeRepr that corresponds to Boolean.

Note that in the macro Foo is actually a type-parameter, so I can only look up the method by its name and I don't have any further info about it.

Thanks


Solution

  • I am using something like:

    def returnTypeOf(tpe: TypeRepr, method: Symbol): Type[?] =
      tpe.memberType(method).widenByName match {
        case lambda: LambdaType => lambda.resType.asType
        case out                => out.asType
      }
    

    It's basically an existential type, which is hard to work with, so I am usually using it with:

    trait ExistentialType {
      type Underlying
      implicit val Underlying: Type[Underlying]
    }
    
    val someType = new ExistentialType {
      val Underlying = returnTypeOf(repr, method).asInstanceOf[Type[Underlying]]
    }
    
    import someType.Underlying as SomeType
    
    Type.of[SomeType]
    TypeRepr.of[SomeType]
    
    term.asExprOf[SomeType[
    

    etc

    EDIT if you want to expand it recursively you need to use

    def returnTypeOf(tpe: TypeRepr, method: Symbol): Type[?] = {
      def loop(tpe: TypeRepr): Type[?] = tpe match {
        case  lambda: LambdaType => loop(lambda.resType)
        case out => out.asType
      }
      loop(tpe.memberType(method).widenByName)
    }
    

    There is support for such things in public reflection API, it's one of these things that do have API - in compiler's internals, which are not public, are not set in stone, and are not intended to be used by users. If you want a stable way of using features like this, you have to implement them yourself on top of the raw quotes.reflect API.