summaryrefslogtreecommitdiff
path: root/test/files/pos/t8947
diff options
context:
space:
mode:
authorJason Zaugg <jzaugg@gmail.com>2014-10-30 11:48:08 +1000
committerJason Zaugg <jzaugg@gmail.com>2014-10-30 11:48:08 +1000
commitd056439a2762b342f66759de654f0dadb01f5e9a (patch)
tree27409f6ecd5ce1ebd855b8b5c2e4a346b881152f /test/files/pos/t8947
parentbdae51d6a8f18a5456a32c350cb551d42a3cb6c6 (diff)
downloadscala-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')
-rw-r--r--test/files/pos/t8947/Client_2.scala1
-rw-r--r--test/files/pos/t8947/Macro_1.scala41
2 files changed, 42 insertions, 0 deletions
diff --git a/test/files/pos/t8947/Client_2.scala b/test/files/pos/t8947/Client_2.scala
new file mode 100644
index 0000000000..1a5082a2f9
--- /dev/null
+++ b/test/files/pos/t8947/Client_2.scala
@@ -0,0 +1 @@
+object Test { X.extractor } \ No newline at end of file
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.
+
+}