Hi all,
I wanted to slack off and not post this, but that's the second
accident during the last two days (and one of those accidents
disrupted the demo last week!). The question is: how do we help people
detect unintended by-reference sharing of trees when generating code
in macros?
This mistake is very easy to make, e.g. take a look at
https://gist.github.com/4234441.
In that code, the author iterates over methods of a class/trait and
creates an anonymous wrapper over an instance passed to the macro,
which logs calls to those methods and redirects the calls to the
underlying instance.
Here's how the wrapper methods are generated:
def wrap_impl...(a: c.Expr[A])...
...
val methods = wrapped.declarations.collect { case m:
MethodSymbol ... => ... Select(a.tree,
m.name) ... }
As a result, all generated `methods` will share a reference to the
tree that represents the argument to the macro. In this macro this
most likely won't be a problem. However if one of the methods
underwent special treatment, e.g. if it assigned manually crafted
types to the generated code, then through `a.tree` those assignments
would propagate to all the methods because ***trees are mutable***,
potentially leading to very cryptic errors.
The correct course of action here, from what I understand (Paul please
correct me if I'm overlooking something), is to use `duplicate`, as in
`a.tree.duplicate`, which creates structure-wise and attribute-wise
clone of the source tree.
So here's the question. How do we detect such situations and ideally
crash immediately? Before wrapping up, I'd like to mention that it's
not as obvious as keeping track of hashes for trees being typechecked,
because some tree might be typechecked in a throw-away context and
then inserted into some other context.
P.S. Back then when hacking Slick, Chris and I had a problem with
resetAllAttrs which unexpectedly stripped a tree shared by reference
of its attrs. Now it's no longer a danger, because resetAllAttrs has
been changed to not operate in-place, but this makes errors even more
subtle and unexpected.
Cheers,
Eugene