diff options
author | Jason Zaugg <jzaugg@gmail.com> | 2014-10-30 11:48:08 +1000 |
---|---|---|
committer | Jason Zaugg <jzaugg@gmail.com> | 2014-10-30 11:48:08 +1000 |
commit | d056439a2762b342f66759de654f0dadb01f5e9a (patch) | |
tree | 27409f6ecd5ce1ebd855b8b5c2e4a346b881152f /test/files/pos/t8947/Macro_1.scala | |
parent | bdae51d6a8f18a5456a32c350cb551d42a3cb6c6 (diff) | |
download | scala-d056439a2762b342f66759de654f0dadb01f5e9a.tar.gz scala-d056439a2762b342f66759de654f0dadb01f5e9a.tar.bz2 scala-d056439a2762b342f66759de654f0dadb01f5e9a.zip |
SI-8947 Avoid cross talk between tag materializers and reify
After a macro has been expanded, the expandee are expansion are
bidirectionally linked with tree attachments. Reify uses the back
reference to replace the expansion with the expandee in the reified
tree. It also has some special cases to replace calls to macros
defined in scala-compiler.jar with `Predef.implicitly[XxxTag[T]]`.
This logic lives in `Reshape`.
However, the expansion of a macro may be `EmptyTree`. This is the case
when a tag materializer macro fails. User defined macros could do the
also expand to `EmptyTree`. In the enclosed test case, the error
message that the tag materializer issued ("cannot materialize
class tag for unsplicable type") is not displayed as the typechecker
finds another means of making the surrounding expression typecheck.
However, the macro engine attaches a backreference to the materializer
macro on `EmpytyTree`!
Later, when `reify` reshapes a tree, every occurance of `EmptyTree`
will be replaced by a call to `implicitly`.
This commit expands the domain of `CannotHaveAttrs`, which is mixed
in to `EmptyTree`. It silently ignores all attempts to mutate
attachments.
Unlike similar code that discards mutations of its type and position,
I have refrained from issuing a developer warning in this case, as
to silence this I would need to go and add a special case at any
places adding attachments.
Diffstat (limited to 'test/files/pos/t8947/Macro_1.scala')
-rw-r--r-- | test/files/pos/t8947/Macro_1.scala | 41 |
1 files changed, 41 insertions, 0 deletions
diff --git a/test/files/pos/t8947/Macro_1.scala b/test/files/pos/t8947/Macro_1.scala new file mode 100644 index 0000000000..4a5de3decb --- /dev/null +++ b/test/files/pos/t8947/Macro_1.scala @@ -0,0 +1,41 @@ +import language.experimental.macros +import scala.reflect.macros._ +import blackbox.Context + +object X { + + def classTagOrNull[T](implicit t: reflect.ClassTag[T] = null) = t + // the failed search for ClassTag[T] does not issue a visible + // error as we fall back to the default argument. But, the + // macro engine things we have expanded the macro `materializeClassTag[D]()` + // to `EmptyTree`, and then attaches a backreference from the expansion + // to the expandee. This is the `MacroExpansionAttachment` tree attachment. + def foo[D] = classTagOrNull[D] + + def extractor: Any = macro X.extractorMacro + def extractorMacro(c: Context): c.Expr[Any] = { + // Later, in reify, an unrelated use of `EmptyTree` in the AST representing + // the argument is now treated as a macro expansion which should be rolled + // back in the tree we reify! This ends up generating a call to `implicitly` + // which leads to an ambiguous error. + // + // Any macro call that expands to EmptyTree could have triggered this problem. + c.universe.reify(new { def something(data: Any) = ??? }) + } + + // Workarounds: + // + // 1. Use quasiquotes rather than `reify`. (But, beware to fully qualify all references, e.g. `_root_.scala.Predef.???`) + // 2. Avoid failed ClassTag lookups (e.g. in the original bug report, annotate the type argument to `map`) + // 3. In the macro implementation, just before calling the `reify` macro, you could call another macro + // + // def prepareReify = macro prepareReifyImpl + // def prepareReifyImpl(c: Context) = { + // val symtab = c.universe.asInstanceOf[reflect.internal.SymbolTable] + // symtab.EmptyTree.setAttachments(symtab.NoPosition) + // } + // + // To make this visible to the macro implementaiton, it will need to be compiled in an earlier stage, + // e.g a separate SBT sub-project. + +} |