I have a macro annotation which is intended to be applied to class definitions. Its purpose is an almost-but-not-quite serialization tool. It inspects the class's constructor parameters and then creates a factory method on the companion object which in turn supplies values for the parameters. It needs to know the types of the parameters in order to do this, so I've been calling Context.typeCheck on them.
The problem occurs when the annotated class's constructor takes a parameter of the same type as itself, or in other similar situations (for instance, if type A and type B both are annotated, and A has a parameter of B, and B has a parameter of A. Type parameters applied to the formal parameters count also). Any of these situations will result in the annotation being recursively invoked until StackOverflowError occurs.
I tried using "withMacrosDisabled=true" as an argument to c.typeCheck, and while this solves the problem, it introduces a different one. If the type being checked has not been previously seen, then the compiler remembers its definition, and its macros are never invoked at all. This isn't a problem for the self-reference case, but it does happen in the mutual reference case.
So I'm stuck. Is there a workaround? Can I solve this with c.openMacros?
Another option, if available, is that I don't strictly need the full definition of the type, I could get by with just its fully qualified name (scala.xml.NodeSeq instead of just NodeSeq). I get the TypeName in the AST, but these are rarely fully qualified, and I don't know how to get the fully qualified name without doing a full typeCheck.
As a side question, what is "withMacrosDisabled" good for? If using it prevents all macro expansion forever on the types found in the passed tree, not just for the current c.typeCheck, that seems like too big of a hammer. And you can't really use it even if that's actually what you want, because the macro evaluation would then depend on the order the types are encountered in their own source.
Edit: Thinking about it, I think the compiler should ensure that each macro is expanded exactly once. In the case of a cycle, as in my example, at least one of the macros involved would still see an incompletely-processed class, which seems unavoidable in a case like this as it is, in effect, a circular dependency. I guess, a flag on the resulting Type to indicate that macro processing is not final would be the best way to deal with it, but that probably can't be done in Paradise.
This seems to be quite related to the discussion started in Can't access Parent's Members while dealing with Macro Annotations (also see a link to a more lengthy elaboration in my answer over there).
If possible I would like to avoid situations when macros see half-expanded or half-populated types in order to reduce the potential for confusion. Last months I've been thinking about ways to avoid that, but there's been a number of higher-priority distractions, so I haven't gotten far yet.
Two potential ideas that I'm pondering are: 1) come up with a notation to specify effects of macro annotations, so that we don't have to expand macros in order to know what classes and members comprise our program (if that works out, then the macro engine can first precompute the list of members and only then launch macro expansions), 2) figure out a mechanism of specifying how macros depend on program elements, so that expansions are ordered correctly. Just yesterday I've also learned about Backstage Java and David Herman's work on typed hygienic macros - this should also be relevant. What do you think about these directions of thought?
In the meanwhile, while I'm trying to figure out a principled solution to the problem of dependencies, I'm also interested in unblocking your use case by providing a workaround or a patch to paradise that would be immediately useful. Could you elaborate on your project, so that we could come up with a fix?
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With