Can't access Parent's Members while dealing with Macro Annotations
Asked Answered
P

1

8

I am kind of blocked with the following (macro annotation) situation. Suppose I have an annotation called @factory which aims to generate an apply method for the annotated trait in the corresponding companion object. For instance, given the trait A:

@factory
trait A {
  val a1: Int
}

the expected code to be generated is the following one:

object A extends Factory[A] {
  def apply(_a1: Int) = new A {
    val a1 = _a1
  }
}

Now suppose we have a trait B which inherits from A:

@factory
trait B extends A {
  val b1: String
}

which is supposed to generate:

object B extends Factory[B] {
  def apply(_a1: Int, _b1: String) = new B {
    val a1 = _a1
    val b1 = _b1
  }
}

In the latter case, I need to know which are the attributes existing at A, but I don't know how to get any information about them. While dealing with macro annotations I have only access to the B trait AST (as a ClassDef). Although its template contains references to the parents (as TypeTrees), both fields tpe and symbol are empty.

It would be great for me to get access to the A AST. However, I think that's not feasible. Therefore, any symbol or type (pointing either the parent or the current type) would be good enough.

If you want to see more implementation details, I have uploaded the project to https://github.com/jesuslopez-gonzalez/cool-factory. It can generate the apply for the local values.

Peignoir answered 15/10, 2013 at 10:54 Comment(7)
Trees that go into macro annotation arguments are purposefully untyped. However, I think, running c.typeCheck(q"(???: <tree that represents the parent>)").tpe might provide the missing information.Seamaid
@EugeneBurmakoI had already tried that this morning, but it raised an error (not sure right now, but I think it said that type tree wasn't an expression). Anyway, I'll go further with typeCheck tomorrow. By the way, while I was analyzing the macros Context API, I read "provide facilities for exploring the compiler's symbol table". What's the way to do so? Thanks!Peignoir
Sure, that's because you probably did c.typeCheck(<tree that represents the parent>). c.typeCheck expects a term, so it fails if you give it a type.Seamaid
The facilities for exploring the symbol table are implemented in c.mirror.Seamaid
I didn't notice the q in your first comment. You were right, I was passing the raw type to c.typeCHeck instead. Finally, I have used an asInstanceOf[A] as input expression and the type is generated nicely. However, there's still one problem. I can't declare both parent and child together (like this github.com/jesuslopez-gonzalez/cool-factory/blob/master/src/…) because if I do so, typeCheck can't find the parent type. Anyway, please, add a response in order to mark it as correct.Peignoir
Regarding the last problem I mentioned, could it be possible that it only arises when both child and parent belong to the same compilation unit? If that's true, I think I can circumvent the problem by using the whole AST extracted from c.enclosingUnit.Peignoir
The problem is with them being declared in the same lexical scope, not compilation unit. See my answer below.Seamaid
S
12

Trees that go into macro annotation arguments are purposefully untyped. However running c.typeCheck(q"(??? : <tree that represents the parent>)").tpe will provide the missing information. Don't forget to duplicate that tree before typechecking, because c.typeCheck mutates the tree in place, which might be undesireable.

In case when both parent and child are declared in the same non-toplevel scope, there will be a problem of typeCheck not seeing the parent, as c.typeCheck's in macro annotations are performed in parent lexical scope, so that annotations don't get to see half-constructed scopes. Something similar has been reported here: https://github.com/aztek/scala-workflow/issues/2#issuecomment-23947943.

The decision to exclude current scope from typechecking is not a final one. This week I'll be thinking a bit more about how macro annotations should interact with enclosing scopes, and will probably change it to do what you would like it to do. I'd do the change right now, but I need to make sure there won't be any insane behaviour arising from that change.

Seamaid answered 16/10, 2013 at 9:0 Comment(4)
Looking forward for your final decision. Could you please post it here at your earliest convenience? Many thanks!Peignoir
I'm afraid I'll have to delay the answer until the next week. I gave it a shot today and faced some ugly complications that need more than one pair of eyes. If you're interested, please join the discussion at: github.com/aztek/scala-workflow/issues/2.Seamaid
@EugeneBurmako How has the “no access to the local scope” issue evolved?Gebhardt
It hasn't, unfortunately. Somewhere around the time of this question, we in the Scala team have agreed that before proceeding with macro annotations, we need to get regular macros right. This is when scala.meta was born, and that's been consuming most of my dedication ever since.Seamaid

© 2022 - 2024 — McMap. All rights reserved.