Calling trait function in dotty macro

Suppose I have a trait Foo as follows:

trait Foo {
  def foo[T](x: T): Etc = someDefaultImpl(x)
}

This may be extended:

trait SubFoo extends Foo {
  override def foo[T](x: T): Etc = differentImpl(x)
}

How might I write a macro that calls foo? The macro itself won’t have foo in scope, because the macro implementer won’t extend Foo. I tried something like the following:

    def findFoo(owner: Symbol): List[Symbol] =
        if (owner.isNoSymbol) Nil
        else {
          owner.methodMember("foo") ++ owner.companionClass.methodMember("foo") match {
            case Nil => findFoo("foo", owner.maybeOwner)
            case results => results
          }
        }

which will give me the relevant symbol that I can then apply with quoted.Apply. However, doing something like Apply(Ref(fooSymb), List(the, params)) doesn’t work, presumably because foo takes a type parameter (I get an error saying that the method does not take parameters).

I feel like there should be a way to get the typed symbol and have type inference do the work for me, but I could not find a way in the documentation.

Additionally, I was having trouble ensuring that this is the right foo - namely, it should take whatever foo is immediately in scope at the point the code is being rewritten. Ideally, I’d like to do something like

@rewriteFoo
class X {
  object A extends Foo {
    def invokeFoo(x: Int) = // macro will insert a call to Foo.foo here
  }
  object B extends SubFoo {
    def invokeFoo(x: Int) = // macro will insert a call to SubFoo.foo here
  }
}

However, it’s also acceptable if the macro invocation needs to be moved to the individual SubFoo or even invokeFoo calls.

If the macro is invoked incorrectly, I’m happy to either error at macro expansion time or insert a dummy implementation.

1 Like

After some experimentation, I was able to resolve the TypeApply problem by guessing the right type parameter. However, I’m still having trouble summoning the correct symbol into scope.

I need to get the Symbol corresponding to foo in the lexical scope of the macro caller. Using methodMember gives me a compiler assertion failure about missing outer accessor. Minimal reproduction:

main.scala
foobarize.scala

If you were to write the code by hand: have an example input code and rewrite it to output code by hand, how would it look like?

I am asking because (simplifying and generalizing) macro cannot generate the code that would be illegal if you wrote it by hand. If there is no foo in scope, how would you write the code to call it? What would you call it on?

I am happy to error out at expansion time via report.error or even insert a default version (that would be included with the macro or written from scratch).